我已经定义了一个带有一个成员函数的简单类模板。它在类外部定义了一个额外的(显式)特化,也在类外定义。所有在一个头文件中。如果在多个转换单元中包含此标头,则由于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;
};
到目前为止一切都很好。但是如果我将成员函数的定义放在类体中,则链接器错误消失,并且函数可以在不同的转换单元中使用。
// 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在模板上有一些例外我最初认为是原因。但是&#34; Base&#34;的定义类内部/外部的函数在这里有所不同。
答案 0 :(得分:4)
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实施实际上已被破坏:它的行为超出了&#34所允许的范围;不需要诊断&#34;语言规范给出的许可。
您在实验中观察到的行为与以下内容一致:将主要功能模板声明为inline
会自动对该模板inline
进行显式特化。这不应该是这样的。在14.7.3 / 14中,语言规范说
函数模板的显式特化仅在内联时才是内联的 使用内联说明符声明或定义为已删除,和 与其功能模板是否内联无关。