是什么决定了两个函数模板声明是声明同一个模板,还是同名的重载?
答案的开头在 3.5p9 中找到:
两个相同的名称(第 3 条)并且在不同的范围内声明应表示相同的变量、函数、类型、枚举器、模板或命名空间,如果
两个名称都有外部链接,否则两个名称都有内部链接并在同一个翻译单元中声明;和
两个名称都指同一个命名空间的成员或同一个类的成员,而不是继承;和
当两个名称都表示函数时,函数的参数类型列表(8.3.5)是相同的;和
当两个名称都表示函数模板时,签名 (14.5.6.1) 是相同的。
非模板非成员函数的签名是(1.3.17):
签名
<function> 名称、参数类型列表 (8.3.5) 和封闭命名空间(如果有)
[注意:签名用作名称修改和链接的基础。——尾注]
8.3.5p5 节已经定义了两次提到的参数类型列表。该段落描述了如何根据声明的类型调整函数参数的实际类型,用指针替换数组和函数,以及丢弃顶级cv-qualifiers。然后,
转换后的参数类型的结果列表以及省略号或函数参数包的存在与否是函数的参数类型列表。
所以在非模板的情况下,parameter-type-list显然是类型的概念语义列表(可能还有一个花哨的结尾),而不是标记序列或句法构造。正如我们所料,以下是违反 ODR 的,因为两个定义都定义了相同的函数:
void f(int, int*) {}
void f(int p, decltype(p)*) {}
在模板案例中,我们有 (1.3.18):
签名
<function template> 名称、参数类型列表 (8.3.5)、封闭命名空间(如果有)、返回类型和模板参数列表
现在考虑:
template<typename T> void g(int, int*, T, T*) {} // #1
// template<typename T> void g(int p, decltype(p)*, T, T*) {} // #2
template<typename T> void g(int, int*, T q, decltype(q)*) {} // #3
g++ -std=c++0x 版本 4.6.3 抱怨定义 #1 和 #2 定义了相同的函数,但接受 #1 和 #3 作为重载没有问题。(它还认为#3 比#1 更专业,没有办法调用#1,但这是一个切题问题。)#2 和#3 之间的主要区别在于q
类型相关而p
不是类型相关。所以我猜在decltype(q)
模板实例化之前无法确定的含义?标准是否保证这种行为?
对于函数模板,必须允许parameter-type-list的含义包括尚未被实例化替换的模板参数,因此依赖名称等等。但这使得如果可能的话,要知道两个声明是否等价就变得很棘手。
14.5.6.1 第 5-6 段解决了类似的问题,它定义了等效表达式和等效函数模板声明(相同的标记序列,除了不同的声明可能对模板参数使用不同的标识符)、功能等效的表达式和功能等效的函数模板声明(实例化后相同),要求:
如果一个程序包含功能等价但不等价的函数模板声明,则该程序是非良构的;不需要诊断。
第 5 段中的一个示例演示了安全等效的函数模板:
template <int I, int J> void f(A<I+J>); // #1
template <int K, int L> void f(A<K+L>); // same as #1
第 7 段中的一个例子表明违反了该规则:
// Ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);
但这不适用于g
上面的示例函数。 T*
并且decltype(q)*
在类型等价的一些类似定义下可能被认为在功能上是等价的,但第 14.5.6.1 节只说明了表达式的替换,而不是类型。