C++ GCC、Clang和IBM在如何执行依赖于模板参数的名称查找方面存在分歧。哪一个是对的?
考虑我在IBM网站上找到的这个例子:C++ GCC、Clang和IBM在如何执行依赖于模板参数的名称查找方面存在分歧。哪一个是对的?,c++,templates,argument-dependent-lookup,C++,Templates,Argument Dependent Lookup,考虑我在IBM网站上找到的这个例子: #include <iostream> using namespace std; void f(double) { cout << "Function f(double)" << endl; } template<class T> void g(T a) { f(123); h(a); } void f(int) { cout << "Function f(int)" <<
#include <iostream>
using namespace std;
void f(double) { cout << "Function f(double)" << endl; }
template<class T> void g(T a) {
f(123);
h(a);
}
void f(int) { cout << "Function f(int)" << endl; }
void h(double) { cout << "Function h(double)" << endl; }
void i() {
extern void h(int);
g<int>(234);
}
void h(int) { cout << "Function h(int)" << endl; }
int main(void) {
i();
}
其基本原理是在实例化i()
之前执行依赖于模板参数的名称查找,因此它查找h(double)
,而不是h(int)
Function f(double)
Function h(int)
GCC似乎是在编译完所有其他内容后在模板中查找依赖于模板参数的名称,因此它同时查找h(double)
和h(int)
,并且更喜欢后者ibm_example.cc:8:3: error: use of undeclared identifier 'h'
h(a);
^
ibm_example.cc:16:3: note: in instantiation of function template specialization 'g<int>' requested here
g<int>(234);
^
1 error generated.
ibm_example.cc:8:3:错误:使用未声明的标识符“h”
h(a);
^
ibm_example.cc:16:3:注意:在函数模板专门化的实例化中,此处请求“g”
g(234);
^
生成1个错误。
Clang似乎在声明模板时查找模板中与模板参数相关的名称,因此它既找不到h(double)
也找不到h(int)
哪一个是对的?他们都是对的。不,真的,继续读
template<class T> void g(T a) {
f(123);
h(a);
}
模板无效g(ta){
f(123);
h(a);
}
这里,f
是一个非从属名称,但根据14.6.2/1,h
是一个从属名称<代码>f被查找
模板定义中使用的非依赖名称可以使用常用的名称查找找到,并绑定在
使用它们的要点
f
立即被查找并绑定到void f(double)
,这是在该点上唯一可见的f
根据14.6.4.1,void g(int)
的实例化点位于使用它的void i()
的定义之后
[…]否则,这种专门化的实例化点
紧跟在引用专门化的命名空间范围声明或定义之后
这意味着解析依赖名称的源是在模板void g(ta)
的定义中可见的声明和“来自与实例化上下文(14.6.4.1)和定义上下文(14.6.4)中的函数参数类型相关联的名称空间的声明”
但是,由于int
是基本名称类型,关联名称空间集为空(3.4.2)(不,甚至不包括全局名称空间),根据14.6.4.2,只能使用关联名称空间进行查找,才能使用模板实例化上下文,普通的非限定名称查找只能使用在模板定义上下文中可见的名称。这证实了14.6.4中所述的内容
现在,奖励积分。14.6.4.2接着说:
如果调用的格式不正确或找到更好的匹配项,则相关名称空间中的查找会考虑所有转换单元中这些名称空间中引入的具有外部链接的所有函数声明,而不仅仅是考虑在模板定义和模板实例化上下文中找到的声明,那么程序有未定义的行为
调用的格式不正确,因为查找失败(此处不适用有关关联名称空间的部分),因此行为未明确定义,因此可能会发生任何情况。因此,所看到的行为均未显示不符合标准,尽管在我看来,Clang的诊断似乎最符合标准
(所有参考ISO/IEC 14882:2011。)GCC 4.5.1 FWIW可能重复无更改。@Foo:这绝对不是那个问题的重复——那个问题是关于过载解决方案,这个问题是关于范围以及定义点上可见的符号。这是一个很好的问题!我们可以用不同的绑定来解释关于未定义行为的部分,这对我来说似乎更有意义:“如果{{调用的格式不正确}或{找到更好的匹配}在相关名称空间中的查找考虑了所有转换单元中这些名称空间中引入的具有外部链接的所有函数声明},…那么程序有未定义的行为“,”或者用不同的措辞(段落的后面部分“如果{{调用将是格式错误的}或者{将找到更好的匹配}在相关名称空间中进行查找,而不仅仅是考虑在模板定义和模板实例化上下文中找到的声明},则程序具有未定义的行为。“。此绑定对我来说更有意义,因为编译器(与链接器相反)无法查看多个翻译单元,因此该标准不需要对以下情况进行诊断:当实现执行此操作时,调用将不明确/格式错误或找到更好的匹配。实际上,我认为此规则可以被视为ODR/规范其他部分中规则的扩展,这些规则未定义如果多个实例化中的任何一个具有不同的行为。在这个词绑定下,如果模板使用由所有单个实例化上下文的联合组成的实例化上下文进行实例化,那么我们将规则undefined,如果模板具有不同于一个/多个单个实例化的函数调用行为当然,我只能评估什么样的解释对我来说更有意义。这些规则制定时我不在场。为了清晰起见,如果标准中有一个项目符号列表会更好。现在,人们不能依赖它是否意味着“如果它是不正确的…,…行为是未定义的。”或者它是否意味着“如果它的格式不正确…在关联的名称空间中进行了查找…,则行为未定义。”.FWIW,GCC4.7 trunk现在符合规则,行为类似于Clang。
template<class T> void g(T a) {
f(123);
h(a);
}