为什么不专门化功能模板? (问答)

Why not specialize function templates? (Q&A)

我会尽量把这个问题整理成Q&A,希望有同仁能有所借鉴。

首先,让我们先回答一个简单的问题,这个程序的输出是什么?

#include <iostream>

template<class T>
void f(T) { std::cout << 1; }

template<>
void f<>(int*) { std::cout << 2; }

template<class T>
void f(T*) { std::cout << 3; }

int main() {
    int *p = nullptr; 
    f( p );
}

如果您的答案是 3,恭喜您可以跳过此问答。

对于其他人:

我个人认为输出应该是2,如果你到了这一步,我猜你也是。
现在让我们深入一点。

名称 f 被两个函数模板 void f(T)void f(T*) 重载。请注意,重载解析仅考虑函数模板,而不考虑显式特化 void f<>(int*)!对于重载决议,首先我们推导出每个函数模板的模板参数,第一个得到 T = int *,第二个得到 T = int

两种功能模板都可行,但哪个最好?根据 [over.match.best]§16.3.3¶1,一个函数模板比​​另一个更专业的函数模板更匹配:

a viable function F1 is defined to be a better function than another viable function F2 if (...) the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 17.5.6.2

这里引用偏序的过程有点长,不是本题的重点。但总而言之,f(T*) 接受的任何内容也会被 f(T) 接受,但反之则不然。所以f(T*)更专业。

现在我们知道函数模板 void f(T*) 是通过重载决议选择的,我们可以开始考虑特化了。 void f<>(int*) 专门化了哪个函数模板? [temp.expl.spec]§17.7.3¶3:

A declaration of a function template (...) being explicitly specialized shall precede the declaration of the explicit specialization.

所以显式特化 void f<>(int*)void f(T) 的特化,因为这是它之前唯一的函数模板声明。然而,重载决议选择了另一个函数模板,void f(T*),我们改为调用一个隐式实例化它的特化,打印 3.

更多请参考Herb Sutter的article