内联模板专业化

Inlining Template Specialization

如果我有一个 header 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'ing 专业化来解决这个问题。我的问题是,为什么我需要这样做?如果模板不违反 ODR,为什么要专门化?

显式特化不是隐式内联。它必须显式内​​联。

[temp.expl.spec]/12

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

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

 — end example ]

所以你必须这样做,因为标准说你必须这样做。

模板免于ODR 的原因很简单,没有其他选择。

从编译器的角度来看,模板不是 "tangible end-product"。模板的实现必须随身携带,以便在使用时可以扩展为可编译代码。因此,它必须位于头文件中,来自不同编译单元的重复定义是不可避免的。既然不可避免,标准就做出妥协,免除ODR。

函数是可以很容易地编译成目标代码的最终产品,因此编译器不愿意看到可能存在冲突的定义,即使完全有可能比较代码并在代码相同时继续进行。然而,编译器决定他们懒得做这样的额外检查,因此标准禁止多重定义。

现在,模板函数的 explicit/full 特化 事实上 是一个函数,而不是模板 - 因为所有缺失的部分已被填补,不再需要携带专门功能的定义。相比之下,部分 特化是事实上 模板,因为它的实现仍然需要在编译期间进行。因此,部分模板特化享受从模板继承的豁免,而 explicit/full 特化则不然。