Tim*_*ell 22 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)" << 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();
}
Run Code Online (Sandbox Code Playgroud)
它会打印什么?
我在这里修改了这个例子的IBM文档说它会打印:
Function f(double)
Function h(double)
Run Code Online (Sandbox Code Playgroud)
这个的基本原理是模板参数依赖的名称查找是在实例化之前执行的i(),所以它找到h(double)但不是h(int).
当我使用GCC 4.4.1编译它时,它打印:
Function f(double)
Function h(int)
Run Code Online (Sandbox Code Playgroud)
GCC似乎在模板中进行查找模板参数相关的名称一切已经编译之后,所以它找到既h(double)和h(int),并倾向于后者.
当我使用Clang 2.8编译它时,它无法编译.编译器错误是:
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.
Run Code Online (Sandbox Code Playgroud)
Clang似乎在模板声明模板的位置查找模板参数相关的名称,因此它既h(double)没有找到也没有找到h(int).
哪一个是对的?
CB *_*ley 13
他们都是正确的.不,真的,请继续阅读......
template<class T> void g(T a) {
f(123);
h(a);
}
Run Code Online (Sandbox Code Playgroud)
这里f是非依赖名称,但是h根据14.6.2/1是依赖名称.f抬起头来
模板定义中使用的非依赖名称可以使用通常的名称查找找到,并在使用它们时绑定.
f立即查找并绑定void f(double),唯一f可见的点.
根据14.6.4.1,实例化的重点void g<int>(int)是在定义之后void i(),使用它的地方.
[..]否则,这种特化的实例化点紧跟在命名空间范围声明或引用特化的定义之后.
这意味着解析依赖名称的源是在定义中可见的template<class T> void g(T a)声明和"与实例化上下文(14.6.4.1)和定义上下文中的函数参数类型相关联的名称空间的声明"(14.6.4) .
但是,因为int是一个fundemantal类型,关联的命名空间集是空的(3.4.2)(不,甚至不包括全局命名空间),并且根据14.6.4.2,它只使用可以使用模板的关联命名空间进行查找实例化上下文,正常的非限定名称查找只能使用模板定义上下文中可见的内容.这证实了14.6.4中的内容.
现在,奖励点.14.6.4.2继续说:
如果调用结果不正确或者找到更好的匹配,那么相关命名空间中的查找会考虑在所有翻译单元中的那些名称空间中引入外部链接的所有函数声明,而不仅仅是考虑模板定义和模板中的那些声明实例化上下文,然后程序有未定义的行为.
呼叫是生病形成,因为查找失败(约相关的命名空间的部分在这里不适用),这样的行为被明确不确定的,所以,只要可能发生.因此,所见的行为都没有显示出与标准不符的情况,尽管对我而言,Clang的诊断似乎最符合标准.
(所有参考文献ISO/IEC 14882:2011.)