无法理解 int 和用户定义类型之间的名称查找差异 - 可能与 ADL 相关
Can't understand name lookup differences between an int and a user defined type - perhaps ADL related
为什么下面的代码可以编译:
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 有适当的两阶段名称查找...
第一个样本有效,因为ADL对模板定义中的依赖名name lookup生效;这使得找到函数 bar
成为可能。 (bar(in)
取决于模板参数 T
。)
(强调我的)
For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).
并且 ADL 不适用于基本类型,这就是第二个示例失败的原因。
奇怪的不是 int
示例编译失败,而是 type
示例编译失败,因为 bar
是在 foo
之后定义的。这是由于 [temp.dep.candidate](见第三段)。
模板的二次编译
当编译器解析和编译模板 class 或函数时,它分两次查找标识符:
- 模板参数独立名称查找:可以检查不依赖于模板参数的所有内容。这里,由于
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):
Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
两遍编译、ADL 查找和非限定 ID 查找
在您的情况下,这三种机制会发生冲突。见 [temp.dep.candidate]:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the
template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the
template definition context or the template instantiation context are
found.
因此,使用 foo(type())
不合格 ID 查找开始并完成查找 "in either the template definition context or the template instantiation".
foo(42)
、42
是基本类型,不考虑ADL,只考虑"definition context"。
为什么下面的代码可以编译:
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 有适当的两阶段名称查找...
第一个样本有效,因为ADL对模板定义中的依赖名name lookup生效;这使得找到函数 bar
成为可能。 (bar(in)
取决于模板参数 T
。)
(强调我的)
For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).
并且 ADL 不适用于基本类型,这就是第二个示例失败的原因。
奇怪的不是 int
示例编译失败,而是 type
示例编译失败,因为 bar
是在 foo
之后定义的。这是由于 [temp.dep.candidate](见第三段)。
模板的二次编译
当编译器解析和编译模板 class 或函数时,它分两次查找标识符:
- 模板参数独立名称查找:可以检查不依赖于模板参数的所有内容。这里,由于
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):
Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
两遍编译、ADL 查找和非限定 ID 查找
在您的情况下,这三种机制会发生冲突。见 [temp.dep.candidate]:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the template definition context or the template instantiation context are found.
因此,使用 foo(type())
不合格 ID 查找开始并完成查找 "in either the template definition context or the template instantiation".
foo(42)
、42
是基本类型,不考虑ADL,只考虑"definition context"。