无法理解int和用户定义类型之间的名称查找差异 - 可能与ADL相关

时间:2017-09-15 14:38:41

标签: c++ templates language-lawyer argument-dependent-lookup name-lookup

为什么以下代码编译:

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有正确的两阶段名称查找......

2 个答案:

答案 0 :(得分:4)

第一个样本有效,因为ADL对模板定义中的从属名称的name lookup生效;这使得找到函数bar成为可能。 (bar(in)取决于模板参数T。)

(强调我的)

  

对于模板定义中使用的依赖名称,将推迟查找,直到知道模板参数为止,此时ADL检查从模板定义上下文以及模板实例化上下文中可见的函数声明,而非ADL查找仅检查从模板定义上下文可见的函数声明(换句话说,在模板定义之后添加新函数声明除了通过ADL之外不会使其显示 )。

并且ADL不适用于基本类型,这就是第二个样本失败的原因。

答案 1 :(得分:4)

奇怪的部分并不是int示例无法编译,而是type示例在bar之后定义foo之后所做的事情。这是由于[temp.dep.candidate](见第三段)。

模板的两遍编译

当编译器解析并编译模板类或函数时,它会在两遍中查找标识符:

  • 与模板参数无关的名称查找:可以检查不依赖于模板参数的所有内容。这里,由于bar()取决于模板参数,因此不做任何事情。此查找在定义时完成。
  • 依赖于模板参数的名称查找:现在可以在传递#1中查找无法查找的所有内容。此查找在实例化时完成。

在传递#2期间出现错误。

ADL查找

当查找函数名时,它在当前上下文和参数类型中完成。例如,以下代码有效,尽管在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查找)是用于在函数调用表达式中查找非限定函数名称的规则集,包括对重载运算符的隐式函数调用。除了通常的非限定名称查找所考虑的范围和名称空间之外,还会在其参数的名称空间中查找这些函数名称。

双程编译,ADL查找和非限定ID查找

在您的情况下,这三种机制相互冲突。见[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;