Search code examples
c++templatesstaticc++03misra

Static function template and MISRA C++


The following function template with specializations should be used within the same .cpp file only, so I’d like to make it static. The following code compiles (without warnings) using both MS Visual C++ 2008 and GCC 4.8.1 and works as intended. (Adding static to the beginning of lines 5 and 11 would cause GCC to issue an error but not MSVC.)

 1  template <class T>
 2  static bool foo(const T param);
 3
 4  template <>
 5  bool foo<int>(const int param)
 6  {
 7      return doSomethingWithInt(param);
 8  }
 9
10  template <>
11  bool foo<bool>(const bool param)
12  {
13      return doSomethingWithBool(param);
14  }

However, MISRA C++ checker complains:

  • (MISRA2008.3-3-2) Apply the static keyword to declaration of 'foo' (1)
  • (MISRA2008.3-3-2) Apply the static keyword to declaration of 'foo' (5)
  • (MISRA2008.2-10-5-b) Identifier 'foo' is being reused (5)
  • (MISRA2008.3-3-2) Apply the static keyword to declaration of 'foo' (11)
  • (MISRA2008.2-10-5-b) Identifier 'foo' is being reused (11)

I tried to find out what’s wrong and found a hint in a C++ standard quote:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

  • For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found.

Does it mean that the compilers discard the static specification and there is no way to actually make static function templates in C++03?


Solution

  • Explicit specializations enable the definition of a function (or class) to change based on the template arguments the template is specialized with. They are not "new declarations".

    GCC is correct to warn for the use of static on the explicit specializations 7.1.1/1:

    A storage-class-specifier shall not be specified in an explicit specialization (14.7.3) or an explicit instantiation (14.7.2) directive.

    Therefore, it seems that the advice from your MISRA checker to apply 'static' is wrong for 5 and 11 and I would also question that foo is somehow being reused. There is just one entity foo that happens to have different definitions.

    A function with internal linkage is not visible outside of this translation unit. An explicit specialization is only considered once the primary template itself has been selected by overload resolution.

    Consider the following:

    template <typename T>
    void f (T);              // #1
    
    template <>
    void f<int*> (int*);     // #2
    
    template <typename T>
    void f (T*);             // #3
    
    void b() {
      int * i;
      f(i);                  // Calls #3
    }
    

    Lookup for f finds two templates, #1 - f(T) and #3 - f(T*). T is deduced to int* for #1 and int for #3 (14.8.2). Overload resolution takes place with the specializations #1 -> f(int*) and #3 -> f(int*). Neither is a best match so partial ordering (14.5.6.2) takes place. The result of partial ordering is that #3 is more specialized than #1. The compiler therefore chooses #3 as the best match. NB: The explicit specialization has taken no part in any of the above steps.

    If there wasn't an #3. Then #1 would have been selected as the best match by overload resolution. Then the compiler searches the list of specialziations. The deduced argument list int* matches the argument list used in the explicit specailziation and so the definition #2 is called.

    Regarding the quoted paragraph:

    • For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found.

    This restriction originated back when templates could be exported (C++ '03 14/6). The idea was to allow templates to be defined outside of the current translation unit. This lookup restriction would have helped ensure that changing a non-exported template to be exported would not result in a different meaning program.

    Regarding your question on what this means for static function templates and C++ '03, well the reality is that only one compiler vendor that I know of ever implemented exported templates in full. There's a good chance that most compiler vendors followed the C++ 11 advice for a long time now anyway. From a MISRA compliance perspective, the best option is to follow the advice in metal's comment to your question. Place the template in an unnamed namespace.

    In C++ '03, the names will be uncallable from outside of the translation unit and for C++ '11 onwards they have internal linkage implicitly (3.5/4):

    An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage.