我定义了一个带有一个成员函数的简单类模板。它是在类外定义的,具有额外的(显式)专门化,也在类外进行定义。一个头文件。如果在多个翻译单元中包含此标头,则会由于"一个定义规则"而导致链接器错误。
// 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;
};
到目前为止一切都很好。但是,如果我把成员函数的定义放在类主体中,链接器错误就会消失,函数可以在不同的翻译单元中使用。
// Header with a template
template <class T>
class TestClass
{
public:
TestClass() {};
~TestClass() {};
bool MemberFunction()
{
return true;
}
};
template <>
bool TestClass<double>::MemberFunction()
{
return true;
};
我的问题是为什么它会这样工作?我使用MSVC 2012。ODR在模板上有一些例外,这是我最初认为的原因。但是类内部/外部的"Base"函数的定义在这里起了作用。
14.7/5表示
5对于给定的模板和给定的模板参数集,
- 显式实例化定义应在程序中最多出现一次
- 一个程序中应最多定义一次显式专业化(根据3.2),以及
- 显式实例化和显式专门化的声明都不应出现在程序中,除非显式实例化遵循显式专门化的声明
一个不需要实现来诊断违反此规则的情况。
第二个要点适用于您的案例。3.2中定义的ODR说明了同样的事情,尽管是以较少蒸馏的形式
无论在何处以及如何定义成员函数的非专用版本,专用版本定义
template <> bool TestClass<double>::MemberFunction()
{
return true;
};
必须进入.cpp
文件。如果保留在头文件中,一旦头包含在多个翻译单元中,就会产生ODR冲突。GCC可靠地检测到该违规行为。MSVC在这方面似乎不那么可靠。但是,正如上面引用的那样,不需要实现来诊断违反此规则的情况。
头文件应该只包含该专门化的非定义声明
template <> bool TestClass<double>::MemberFunction();
在MSVC中,错误的出现或消失取决于看似无关的因素,比如函数的非专用版本是如何定义的,这一定是MSVC编译器的一个怪癖。
经过进一步的研究,似乎MSVC的实现实际上已经被打破了:它的行为超出了";不需要诊断";语言规范给出的权限。
您在实验中观察到的行为与以下内容一致:将主函数模板声明为inline
会自动使该模板的显式专用化inline
。这不应该是那样的。14.7.3/14中的语言规范显示
函数模板的显式专用化只有在使用内联说明符声明或定义为已删除,并且独立于其函数模板是否内联。