未预先声明的函数调用适用于类类型,但不适用于原始类型

时间:2018-08-18 05:21:08

标签: c++ c++17 argument-dependent-lookup

在以下代码中

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。那为什么代码可以使用类类型呢?

2 个答案:

答案 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>