23

下面的程序为什么这个调用swap()模糊?

#include <algorithm> 
#include <utility> 
#include <memory> 

namespace my_namespace 
{ 


template<class T> 
void swap(T& a, T& b) 
{ 
    T tmp = std::move(a); 
    a = std::move(b); 
    b = std::move(tmp); 
} 

template<class T, class Alloc = std::allocator<T>> 
class foo {}; 

} 

int main() 
{ 
    my_namespace::foo<int> *a, *b; 

    using my_namespace::swap; 

    swap(a,b); 

    return 0; 
} 

导致两个g++clang发出我的系统上执行以下编译器错误:

$ clang -std=c++11 swap_repro.cpp -I. 
swap_repro.cpp:28:3: error: call to 'swap' is ambiguous 
    swap(a,b); 
    ^~~~ 
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/bits/algorithmfwd.h:571:5: note: candidate function [with _Tp = my_namespace::foo<int, std::allocator<int> > *] 
    swap(_Tp&, _Tp&) 
    ^
swap_repro.cpp:10:6: note: candidate function [with T = my_namespace::foo<int, std::allocator<int> > *] 
void swap(T& a, T& b) 
    ^
1 error generated. 

$ g++ -std=c++11 swap_repro.cpp -I. 
swap_repro.cpp: In function ‘int main()’: 
swap_repro.cpp:28:11: error: call of overloaded ‘swap(my_namespace::foo<int>*&, my_namespace::foo<int>*&)’ is ambiguous 
    swap(a,b); 
     ^
swap_repro.cpp:28:11: note: candidates are: 
swap_repro.cpp:10:6: note: void my_namespace::swap(T&, T&) [with T = my_namespace::foo<int>*] 
void swap(T& a, T& b) 
    ^
In file included from /usr/include/c++/4.9/bits/stl_pair.h:59:0, 
       from /usr/include/c++/4.9/utility:70, 
       from /usr/include/c++/4.9/algorithm:60, 
       from swap_repro.cpp:1: 
/usr/include/c++/4.9/bits/move.h:166:5: note: void std::swap(_Tp&, _Tp&) [with _Tp = my_namespace::foo<int>*] 
    swap(_Tp& __a, _Tp& __b) 
    ^

我不明白为什么std::swap被视为候选人的过载,但它与foo的使用std::allocator<T>有关。

消除foo的第二个模板参数允许程序无误地编译。

回答

17

因为std::allocator<T>用作模板类型参数,所以std命名空间是ADL的关联名称空间。

[basic.lookup.argdep]/2,子弹2,重点煤矿:

此外,如果T是一个类模板专业化,其相关的 命名空间和类还包括:同类型的模板相关的命名空间和类 提供的参数为 模板类型参数(不包括模板模板参数); 其中任何模板模板参数都是成员的名称空间; 以及用作模板 模板参数的任何成员模板的类都是成员。

...和指针具有相同的组相关联的名称空间/类的,因为它们指向类型:

如果T是指向UU阵列,其相关联的命名空间和 类是那些与U相关的类。

10

关联命名空间的集合是根据参数类型中可见的各种类型确定的。值得注意的是,对于类模板,关联的名称空间包含所有模板参数的关联名称空间。当使用参数相关查找查找非限定函数时,将搜索所有关联的名称空间。

foo<int>模板参数列表实际上是foo<int, std::allocator<int>>,从而拖动命名空间std进来,并已经有用于swap()可以从那里一般的过载。