为什么以下代码编译:
template<typename T>
void foo(T in) { bar(in); }
struct type{};
void bar(type) {}
int main() { foo(type()); }
如果以下情况不符合:
template<typename T>
void foo(T in) { bar(in); }
void bar(int) {}
int main() { foo(42); }
使用GnuC ++ 7进行编译:
a.cpp: In instantiation of 'void foo(T) [with T = int]':
a.cpp:9:20: required from here
a.cpp:2:21: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
void foo(T in) { bar(in); }
~~~^~~~
a.cpp:8:6: note: 'void bar(int)' declared here, later in the translation unit void bar(int) {}
我会假设MSVC会同时编译(就像它一样),但GCC会拒绝这两个,因为GCC / Clang有正确的两阶段名称查找......
答案 0 :(得分:4)
第一个样本有效,因为ADL对模板定义中的从属名称的name lookup生效;这使得找到函数bar
成为可能。 (bar(in)
取决于模板参数T
。)
(强调我的)
对于模板定义中使用的依赖名称,将推迟查找,直到知道模板参数为止,此时ADL检查从模板定义上下文以及模板实例化上下文中可见的函数声明,而非ADL查找仅检查从模板定义上下文可见的函数声明(换句话说,在模板定义之后添加新函数声明除了通过ADL之外不会使其显示 )。
并且ADL不适用于基本类型,这就是第二个样本失败的原因。
答案 1 :(得分:4)
奇怪的部分并不是int
示例无法编译,而是type
示例在bar
之后定义foo
之后所做的事情。这是由于[temp.dep.candidate](见第三段)。
当编译器解析并编译模板类或函数时,它会在两遍中查找标识符:
bar()
取决于模板参数,因此不做任何事情。此查找在定义时完成。在传递#2期间出现错误。
当查找函数名时,它在当前上下文和参数类型中完成。例如,以下代码有效,尽管在f
命名空间中定义了n
:
namespace n { struct type {}; void f(type) {}; }
int main() { n::type t; f(t); } // f is found in ::n because type of t is in ::n
More about ADL (cppreference.com):
依赖于参数的查找(也称为ADL或Koenig查找)是用于在函数调用表达式中查找非限定函数名称的规则集,包括对重载运算符的隐式函数调用。除了通常的非限定名称查找所考虑的范围和名称空间之外,还会在其参数的名称空间中查找这些函数名称。
在您的情况下,这三种机制相互冲突。见[temp.dep.candidate]:
对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则 使用通常的查找规则找到候选函数(3.4.1, 3.4.2)除外:
- 对于使用非限定名称查找(3.4.1)的查找部分,只有来自的外部链接的函数声明 找到模板定义上下文 - 对于使用关联命名空间(3.4.2)的查找部分,只有在外部链接中找到的函数声明 模板定义上下文或模板实例化上下文 找到。
因此,foo(type())
非限定ID查找启动,并且在模板定义上下文或模板实例化&#34; 中完成查找&#34;。
使用foo(42)
,42
是基本类型时,不会考虑ADL,只会考虑&#34;定义上下文&#34; 。