如果我有一个标题foo.h,我把它包含在我的项目中,当它只包含以下内容时,它似乎工作正常:
template<typename T>
void foo(const T param) {
cout << param << endl;
}
但是当我向 foo.h 添加规范化时,我收到一个定义规则 (ODR( 错误:
template<>
void foo(const bool param) {
cout << param << endl;
}
显然,我可以通过inline
专业化来解决这个问题。我的问题是,我为什么需要?如果模板不违反 ODR,为什么专业化?
显式专用化不是隐式内联的。它必须显式内联。
[温度规格]/12
函数或变量模板的显式专用化是 仅当使用内联说明符声明或定义为内联时,才内联 已删除,并且与其函数或变量无关 模板是内联的。[ 示例:
template<class T> void f(T) { /* ... */ } template<class T> inline T g(T) { /* ... */ } template<> inline void f<>(int) { /* ... */ } // OK: inline template<> int g<>(int) { /* ... */ } // OK: not inline
— 结束示例 ]
所以你必须这样做,因为标准说你必须这样做。
模板免于 ODR 的原因很简单,因为没有其他选择。
从编译器的角度来看,模板不是"有形的最终产品"。 模板的实现必须随身携带,以便在使用时可以扩展为可编译的代码。 因此,它必须驻留在头文件中,并且来自不同编译单元的重复定义是不可避免的。 由于这是不可避免的,因此该标准做出了妥协,使他们免于ODR。
函数是可以轻松编译为目标代码的最终产品,因此编译器讨厌看到潜在的冲突定义,即使完全可以比较代码并在代码相同的情况下继续。 然而,编译器认为他们懒得做这种额外的检查,因此标准禁止多个定义。
现在,模板函数的显式/完全专用化实际上是一个函数,而不是模板 - 因为所有缺失的部分都已填充,不再需要携带专用函数的定义。 相比之下,部分专用化实际上是一个模板,因为它的实现仍然需要在编译过程中随身携带。 因此,部分模板专用化享有从模板继承的豁免,而显式/完全专用化则不然。