为什么下面的代码可以编译通过:
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 有适当的两阶段名称查找...
奇怪的部分并不是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
有关 ADL 的更多信息 (cppreference.com) http://en.cppreference.com/w/cpp/language/adl:
参数相关查找,也称为 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 查找并完成查找“在模板定义上下文或模板实例化中”.
With foo(42)
, 42
作为基本类型,不考虑 ADL,仅考虑“定义上下文”被认为。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)