Why is this call to swap() ambiguous?
Asked Answered
B

2

23

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.

Brolly answered 3/12, 2015 at 20:39 Comment(0)
T
19

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 to U or an array of U, its associated namespaces and classes are those associated with U.

Twitty answered 3/12, 2015 at 20:45 Comment(0)
P
11

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.

Prophet answered 3/12, 2015 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.