4

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?

Community
  • 1
  • 1
Melebius
  • 6,183
  • 4
  • 39
  • 52
  • 1
    You can put it in an unnamed namespace: `namespace { template void Foo() { ... } }`. Not sure if the MISRA checker is smart enough to know that will do the same thing. – metal Sep 11 '14 at 13:02
  • 1
    In this case, it is actually counter intuitive to define an explicit template specializations. Regular function overload syntax should be more clear, and adding `static` would work. – jxh Sep 11 '14 at 22:53
  • @jxh Well, it should work with the example above but my actual problem is a bit more complicated. I call my wannabe-static function template from another function template which is not specialized and the call uses explicit template parameter since there is no type-distinguishing parameter. – Melebius Sep 15 '14 at 07:44
  • Which `MISRA checker` are you using? – Andrew Nov 06 '14 at 11:46
  • @Andrew Parasoft C++test 9.2. – Melebius Nov 06 '14 at 16:25
  • Does this answer your question? [The Definitive C++ Book Guide and List](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Pokemon May 04 '20 at 09:31
  • @pforpraphul No, this is a pretty specific question. How does your suggestion cover MISRA rules? – Melebius May 04 '20 at 09:40

1 Answers1

1

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.

Community
  • 1
  • 1
Richard Corden
  • 21,389
  • 8
  • 58
  • 85