The following program
#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;
}
causes both g++
and clang
to issue the following compiler error on my system:
$ 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)
^
I don't understand why std::swap
is being considered as a candidate overload, but it has something to do with foo
's use of std::allocator<T>
.
Eliminating foo
's second template parameter allows the program to compile without error.
Because std::allocator<T>
is used as a template type argument, the std
namespace is an associated namespace for ADL.
[basic.lookup.argdep]/2, bullet 2, emphasis mine:
Furthermore, if
T
is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members.
...and pointers have the same set of associated namespaces/classes as the type they point to:
If
T
is a pointer toU
or an array ofU
, its associated namespaces and classes are those associated withU
.
The set of associated namespaces is determined based on various types visible from the argument types. Notably, for class templates the associated namespaces include the associated namespaces of all template arguments. When looking up unqualified functions using argument dependent look-up all associated namespaces are searched.
The template argument list of foo<int>
is actually foo<int, std::allocator<int>>
, thereby dragging namespace std
into the picture and there is already a general overload for swap()
available from there.
来源:https://stackoverflow.com/questions/34075718/why-is-this-call-to-swap-ambiguous