是什么决定了两个函数模板声明是否声明了相同的模板,或者是否是同名的重载?
答案的开头在3.5p9中找到:
两个相同的名称(第3条)并且在不同的范围内声明,如果
,则表示相同的变量,函数,类型,枚举器,模板或名称空间。
两个名称都有外部链接,或者两个名称都有内部链接,并在同一个翻译单元中声明;以及
这两个名称指的是同一名称空间的成员或同一类的成员,而不是继承。以及
当两个名称都表示函数时,函数的参数类型列表(8.3.5)是相同的;以及
当两个名称都表示功能模板时,签名(14.5.6.1)是相同的。
非模板非成员函数的签名是(1.3.17):
签名
<作用>名称,参数类型列表(8.3.5)和封闭名称空间(如果有)
[注意:签名用作名称修改和链接的基础。 - 结束记录]
两次提到的 parameter-type-list 已在8.3.5p5节中定义。该段描述了如何从声明的类型调整实际类型的函数参数,用指针替换数组和函数,以及丢弃顶级 cv-qualifiers 。然后,
转换后的参数类型的结果列表以及省略号或函数参数包的存在与否是函数的 parameter-type-list 。
所以在非模板的情况下, parameter-type-list 显然是一个类型的概念语义列表(加上可能是一个奇特的结尾),而不是一系列标记或句法结构。正如我们所期望的那样,以下是违反ODR的原因,因为两个定义都定义了相同的功能:
void f(int, int*) {}
void f(int p, decltype(p)*) {}
在模板案例中,我们有(1.3.18):
签名
<功能模板>名称,参数类型列表(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节只说明了表达式的替换,而不是类型。
答案 0 :(得分:2)
标准有一个安静的未明确定义的类型等价规则,主要基于限定名称的语法,但模板参数是参数列表中的位置和该列表的嵌套深度(即它是否是成员模板或封闭类模板。)
typedef会记住特定的依赖类型。然而,14.4p2的decltype类型是一种独特的类型,与T不相同。