4

I have defined a simple class-template with one member function. It is defined outside the class with an additional (explicit) specialization, also defined outside the class. All in one headerfile. If you include this header in multiple translation units you get a linker error due to One-Definition-Rule.

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction();
};

template <class T>
bool TestClass<T>::MemberFunction()
{
    return true;
}

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

Everything fine so far. But If I put the definition of the member function inside the class body, the linker error disappears and the functions can be used throughout different translation units.

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction()
    {
        return true;
    }
};

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

My question is why does it work that way? I use MSVC 2012. ODR has some exceptions on templates what I first thought to be the reason. But the definition of the "Base" function inside/outside the class makes the difference here.

Michbeckable
  • 1,851
  • 1
  • 28
  • 41
  • GCC will issue a linker error for both versions of your code. Explicit specializations of member functions have to be defined only once in the entire program, as required by ODR. If you keep it in the header file, the explicit specialization will produce a multiple-definition error due to ODR violation. – AnT stands with Russia Oct 10 '14 at 18:04

1 Answers1

5

14.7/5 says

5 For a given template and a given set of template-arguments,

  • an explicit instantiation definition shall appear at most once in a program,
  • an explicit specialization shall be defined at most once in a program (according to 3.2), and
  • both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization.

An implementation is not required to diagnose a violation of this rule.

The second bullet applies to your case. The ODR defined in 3.2 says the same thing, albeit in a less distilled form.

Regardless of where and how the non-specialized version of member function is defined, the specialized version definition

template <> bool TestClass<double>::MemberFunction()
{
    return true;
};

has to go into a .cpp file. If kept in the header file, it will produce an ODR violation once the header gets included into more than one translation unit. GCC reliably detect this violation. MSVC seems to be less reliable in that regard. But, as the quote above states, an implementation is not required to diagnose a violation of this rule.

The header file should only contain a non-defining declaration of that specialization

template <> bool TestClass<double>::MemberFunction();

The fact that in MSVC the error appears or disappears depending on such seemingly unrelated factor as how the non-specialized version of the function is defined must be a quirk of MSVC compiler.


After further research, it appears that MSVC implementation is actually broken: its behavior goes beyond what's allowed by the "no diagnostic is required" permission given by the language specification.

The behavior you observed in your experiments in consistent with the following: declaring the primary function template as inline automatically makes the explicit specialization of that template inline as well. This is not supposed to be that way. In 14.7.3/14 the language specification says

An explicit specialization of a function template is inline only if it is declared with the inline specifier or defined as deleted, and independently of whether its function template is inline.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Seems to be a bit more complicated than it needs to be. – John Dibling Oct 10 '14 at 18:22
  • @John Dibling: Why? A template that no longer has any "flexible" parameters is no longer a template. It should obey the same rules that non-template entities obey. I.e. the *definition* of an explicitly specialized function template has to go into `.cpp` file just like the definition of any ordinary function does. – AnT stands with Russia Oct 10 '14 at 18:25
  • Becasue it doesnt seem to me that the core problem here has anything to do with specialization. Simply marking the out-line definition as `inline` fixes the problem, and is precisely what `inline` is for. – John Dibling Oct 10 '14 at 18:32
  • FWIW, I +1'ed the post because I felt it had valuable information and insight. – John Dibling Oct 10 '14 at 18:33
  • Taken another way, the answer to the question, "My question is why does it work that way?" is because methods out-line defined in a header are defined in every TU that includes that file, violating ODR, and marking them `inline` is the solution, IMO. – John Dibling Oct 10 '14 at 18:35
  • @John Dibling: The way I understood the question is that in MSVC compiler the multiple-definition linker error is reported for `bool TestClass::MemberFunction()` definition (i.e. for specialization). But the in-class/out-of class change is applied to the `bool TestClass::MemberFunction()` definition (i.e. to the generic template). These two definitions are independent definitions (albeit somewhat related). So, the behavior of MSVC appears to be illogical: a seemingly unrelated change to one definition triggers an error for another definition. – AnT stands with Russia Oct 10 '14 at 18:56
  • Note that making the generic definition `inline` is not supposed to make the specialized definition `inline`. There's nothing like that in the language spec. The user can make them inilne/non-inline completely independently. This means that ODR enforcement for `bool TestClass::MemberFunction()` should not be affected by any changes made to `bool TestClass::MemberFunction()`. But in case of MSVC it seems to be affected. It is quite possible that the internal reason for that is indeed connected to `inline`. But that would definitely be a quirk of the compiler. – AnT stands with Russia Oct 10 '14 at 18:58
  • Ok, according to your answer AndreyT and this post: http://stackoverflow.com/questions/3052579/explicit-specialization-in-non-namespace-scope the standard says explicit specialization must not be defined more than once AND must be defined in the same scope as the containing template does. You are right, it doesn't matter where the non-spec. member function is defined (inside/outside) class, both is possible and should not affect any issue regarding explicit specializations of this function. MSVC has strange issue here in compiling. – Michbeckable Oct 11 '14 at 13:52
  • Following the standard `gcc` goes the straight way and only allows declaration as @AndreyT stated outside the class (because of scope) in the header file. MSVC further allows some wild techniques here: You can declare the specialized function within class and define it outside (but within the header): http://goo.gl/5GCs8t I tried to just declare in the header: http://goo.gl/ZUXYfJ But had problems with the linker not finding the definition I provided in a .cpp file?! The `inline` keyword here is a nice solution: http://goo.gl/4VNBfD . But the function should be worth inlining. – Michbeckable Oct 11 '14 at 14:07
  • "But had problems with the linker not finding the definition I provided in a .cpp file" - how did you manage to obtain this result? In my experiments MSVC had no trouble finding the definition in .cpp file. – AnT stands with Russia Oct 11 '14 at 14:10
  • I have the declaration of specialized function in Header.h and my Source.cpp looks like this: http://goo.gl/Z19hxc. A call to the specialized function in main.cpp brings the linker error of unresolved symbol. It is template related I think. I tried declaring/defining an ordinary function this way and it works well. Where is my mistake? – Michbeckable Oct 11 '14 at 14:16
  • @Michbeckable: Good find. At this point it is probably safe to conclude that in MSVC declaring the generic version of the template `inline` automatically declares the specialized function `inline` as well. For this reason you have to define the specialized function in the header file as well. This is incorrect behavior. I believe It is a bug in MSVC. However, there's a certain logic in this behavior :) I wonder if the issue is more complicated than I think... – AnT stands with Russia Oct 11 '14 at 14:26
  • @AndreyT: It works if I put the definition directly into main.cpp where the actual call to the function is done. If I put an explicit instantiation of the specialized template class into the template header file, it finally works: `template class TestClass;`! Now the linker finds the specialized function defined in Source.cpp. May you add that point to your answer above, it is important! – Michbeckable Oct 11 '14 at 14:40