//(1)
template <class T>
void f(T) {}
//(2)
template <class T>
void f(T*) {}
//(3)
template <>
void f<>(int*) {}
//(4)
void f(int*) {}
int main()
{
int* p;
f(p); // which function will be called?
return 0
}
我已经知道了这个行为:
令我烦恼的是,顺序很重要(因为它增加了我在编写代码时的不确定性和猜测工作),尤其是我知道如果那些是类特化,那么顺序无关紧要,最专业的类可以被编译器调用。
有人可以向我解释这是如何运作的吗?以及编译器在查找适当的函数特化时遵循的规则是什么?
答案 0 :(得分:5)
基本规则实际上相当简单:函数和函数模板参与重载决策。显式函数模板特化不。
因此,对于调用f(p)
,编译器执行重载决策以在(1),(2)和(4)之间进行选择。 (3)从未考虑过载解析。
重载分辨率选择单个函数或函数模板。 在完成后,如果它选择了一个功能模板,那么然后将考虑该模板的特化。
让我们分析您提供的声明的不同组合。
如果存在(4),将明确选择。当其他条件相同时,非模板函数是比函数模板更好的重载匹配。
假设我们按此顺序只有(1),(2)和(3)。过载分辨率选择在(1)和(2)之间。 (2)更好,因为它更专业。因此,通过重载决策选择函数模板(2)。
然后,编译器查看模板是否具有任何特化。事实上,它确实是T = int
的专业化(3)。所以选择了这种专业化。
现在,让我们说顺序是(1),(3),(2)。同样,重载决策在(1)和(2)之间选择(记住特化,其中(3)是,从不参与重载决策)。 (2)再次被选中。这一次,(3)是(1)的特化,对于T = int*
。这是因为(2)在声明(3)时尚不存在,所以没有(2)专门化。但是(1)没有通过重载决策选择,因此不予考虑。
顺序(2),(3),(1)与(1),(2),(3)相同。顺序(2),(1),(3)与(1),(3),(2)相同。 (1)和(2)的相对顺序无关紧要 - 它是(3)的位置,它控制着它将专门化的模板。
答案 1 :(得分:2)
这取决于是否存在过载(4)。
...这是由于重载决策中的特殊规则:某些模板不具有特殊性的函数优先于函数模板特化,如果超重分辨率规则无法区分这些函数。 E.g。
template <typename T>
void f(T); // A
void f(int); // B
f(4); // Calls B - Conversions of 4 to both parameter types are equivalent
这也适用于您的样本:由于f<int>
(1),f<int*>
(2)和f
(4)的参数类型相同,因此必须选择(4) 。
一旦(4)消失,部分排序开始:它确定(2)更专业,因为
某些任意类型U
无法与T*
匹配(U
不是指针),而
V*
可以匹配一些任意指针类型T
(因为T
将简单地推断为V*
)。
因此(2)中的专业化f<int>
是更好的匹配。
取决于(1)(2)和(3)的声明顺序
您声明了名称f
的两个函数模板重载。但是你声明的显式专业化可以兼顾两者!推导出模板参数,可以推导出f(T)
和f(T*)
,因为它们都可以指向int
。
template <class T> void f(T) {} // (1)
// Specializes f<int*> from (1), (2) not yet declared
template <> void f<>(int*) {}
template <class T> void f(T*) {} // (2)
或者
template <class T> void f(T) {} // (1)
template <class T> void f(T*) {} // (2)
// Specializes f<int> from (2)
template <> void f<>(int*) {}
在后一种情况下,选择(2)因为它比(1)更专业(根据部分排序规则)。您可以通过显式提供模板参数来明确专业化,而不是将它们保留为未指定:
template <>
void f<int>(int*) {} // Can only specialize (2)