问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/26305368/c-define-member-function-outside-template-class-but-in-header