在以下代码中
template <typename T>
void foo(T) {
bar(T{});
}
class Something {};
void bar(Something) {}
int main() {
foo(Something{});
}
(https://wandbox.org/permlink/l2hxdZofLjZUoH4q)
当我们使用foo()
参数调用Something
时,一切都按预期进行,该调用将分派到bar(Something)
重载。
但是当我将参数更改为整数并提供bar(int)
重载时,会出现错误
template <typename T>
void foo(T) {
bar(T{});
}
void bar(int) {}
int main() {
foo(int{});
}
错误:
error: call to function 'bar' that is neither visible in the template definition nor found by argument-dependent lookup
(https://wandbox.org/permlink/GI6wGlJYxGO4svEI)
在类情况下,我还没有在bar()
的定义中在命名空间中定义Something
。表示我没有获得ADL。那为什么代码可以使用类类型呢?
答案 0 :(得分:7)
那为什么代码可以处理类类型?
根据§6.4.2/2.1:
命名空间和类的集合在以下确定 方式:
- 如果T是基本类型,则其关联的名称空间和类集均为空。
因此,在编写g++ -shared -L/usr/lib -L/usr/local/Cellar/boost/1.67.0_1/lib -L/usr/local/Cellar/boost-python3/1.67.0_1/lib/ `python3.6m-config --libs --ldflags` -lboost_python3 -o hello_ext.so hello.o -v
Apple LLVM version 9.0.0 (clang-900.0.38)
Target: x86_64-apple-darwin17.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
"/Library/Developer/CommandLineTools/usr/bin/ld" -demangle -lto_library /Library/Developer/CommandLineTools/usr/lib/libLTO.dylib -dynamic -dylib -arch x86_64 -macosx_version_min 10.13.0 -o hello_ext.so -L/usr/lib -L/usr/local/Cellar/boost/1.67.0_1/lib -L/usr/local/Cellar/boost-python3/1.67.0_1/lib/ -L/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/config-3.6m-darwin -lpython3.6m -ldl -framework CoreFoundation -lpython3.6m -ldl -framework CoreFoundation -lboost_python3 hello.o -lc++ -lSystem /Library/Developer/CommandLineTools/usr/lib/clang/9.0.0/lib/darwin/libclang_rt.osx.a
ld: library not found for -lboost_python3
clang: error: linker command failed with exit code 1 (use -v to see invocation)
时,编译器将具有一组空的名称空间和要考虑的类。因此,对foo(int)
的调用必须失败,因为尚未声明。如果您事先声明了bar
,则代码将编译:
foo(int)
另一方面,在void bar(int);
template <typename T>
void foo(T) {
bar(T{});
}
void bar(int) {}
int main() {
foo(int{});
}
的情况下,(全局)名称空间将成为查找的一部分,因此编译器会主动在名称空间中扫描名为foo(Something)
的函数,该函数可以是用bar
个实例调用。
答案 1 :(得分:4)
在foo
定义中,bar
是一个从属名称,因为它使用依赖于模板参数(T
)的参数来调用。
从属名称解析执行了两次[temp.dep.res]:
在解析从属名称时,将考虑以下来源的名称:
在模板定义时可见的声明。
来自实例化上下文([temp.point])和定义上下文中与函数参数类型关联的名称空间的声明。 在下面,注释显示了实例化的位置:
template <typename T>
void foo(T) { //point of definition of foo
bar(T{});
}
class Something {};
void bar(Something) {}
void bar(int) {}
int main() {
foo(int{});
foo(Something{});
}
//point of instantiation of foo<int>
//point of instantiation of foo<Something>
从定义的角度来看,对于foo<Something>
和foo<int>
,没有bar
可见。
对于foo<Something>
,Something
是一个类,其关联的名称空间是在其中声明该名称空间的名称:全局名称空间。根据第二个项目符号,从实例化点在全局命名空间中执行名称查找,并找到bar(Something)
。
对于foo<int>
,int
是基本类型,它没有任何关联的名称空间。因此,从实例化点起不会进行名称查找,因此找不到bar<int>
。