23

Следующая программаПочему этот вызов обмениваться() неоднозначно?

#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 рассматривается как перегрузка кандидата, но это как-то связано с использованием foostd::allocator<T>.

Устранение второго параметра шаблона foo позволяет программе скомпилировать без ошибок.

ответ

17

Поскольку std::allocator<T> используется как аргумент типа шаблона, пространство имен std является ассоциированным пространством имен для ADL.

[basic.lookup.argdep]/2, пуля 2, курсив мой:

Кроме того, если T является шаблоном класса специализации, связанные с ним пространств имен и классов включают также: пространства имен и классы связанные с типами шаблона аргументы для параметров шаблона (исключая параметры шаблона шаблона); пространства имен, членами которого являются аргументы шаблона шаблона; и классы, в которых любые шаблоны-члены, используемые в качестве шаблона аргументы шаблона являются членами.

... и указатели имеют одинаковый набор связанных пространств имен/классов в качестве типа они указывают:

Если T является указателем на U или массив U, связанные пространства имен и классы связаны с U.

10

Набор связанных пространств имен определяется на основе различных типов, видимых из типов аргументов. Примечательно, что для шаблонов классов связанные пространства имен включают связанные пространства имен всех аргументов шаблона. При поиске неквалифицированных функций с использованием зависимого от аргументов поиска выполняется поиск всех связанных пространств имен.

Шаблон список параметров foo<int> фактически foo<int, std::allocator<int>>, тем самым перетаскиванием имен std в картину, и есть уже общая перегрузка для swap() доступны оттуда.

Смежные вопросы