Почему этот вызов 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
Второй параметр шаблона позволяет программе компилироваться без ошибок.
2 ответа
Так как std::allocator<T>
используется в качестве аргумента типа шаблона, std
namespace - это связанное пространство имен для ADL.
[basic.lookup.argdep] / 2, пуля 2, выделенная мина:
Кроме того, если
T
является специализацией шаблона класса, связанные с ней пространства имен и классы также включают: пространства имен и классы, связанные с типами аргументов шаблона, предоставляемых для параметров типа шаблона (исключая параметры шаблона шаблона); пространства имен, членами которых являются любые аргументы шаблона шаблона; и классы, членами которых являются любые шаблоны-члены, используемые в качестве аргументов шаблона.
... и указатели имеют тот же набор связанных пространств имен / классов, что и тип, на который они указывают:
Если
T
это указатель наU
или массивU
его связанные пространства имен и классы связаны сU
,
Набор связанных пространств имен определяется на основе различных типов, видимых из типов аргументов. В частности, для шаблонов классов связанные пространства имен включают связанные пространства имен всех аргументов шаблона. При поиске неквалифицированных функций с использованием зависимого от аргумента поиска ищутся все связанные пространства имен.
Список аргументов шаблона foo<int>
на самом деле foo<int, std::allocator<int>>
, тем самым перетаскивая пространство имен std
в картину, и уже есть общая перегрузка для swap()
доступно оттуда.