23

Il seguente programmaPerché questa chiamata a swap() è ambigua?

#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; 
} 

provoca sia g++ e clang di emettere il seguente errore del compilatore sul mio sistema:

$ 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) 
    ^

non capisco il motivo per cui std::swap viene considerato come un sovraccarico del candidato, ma ha qualcosa a che fare con l'uso di foo di std::allocator<T>.

L'eliminazione del parametro del secondo modello di foo consente la compilazione del programma senza errori.

risposta

17

Poiché std::allocator<T> viene utilizzato come argomento del tipo di modello, lo spazio dei nomi std è uno spazio dei nomi associato per ADL.

[basic.lookup.argdep]/2, punto 2, l'enfasi è mia:

Inoltre, se T è una specializzazione modello di classe, i suoi associati spazi dei nomi e classi includono anche: gli spazi dei nomi e classi associate ai tipi del modello argomenti forniti per i parametri del tipo di modello (esclusi i parametri del modello di modello); gli spazi dei nomi di cui gli argomenti del template template sono membri; e le classi di cui qualsiasi modello membro utilizzato come modello template sono membri.

... e puntatori hanno lo stesso insieme di namespace associati/classi come tipo essi indicano:

Se T è un puntatore a U o una matrice di U, suoi associati e namespace Le classi sono quelle associate a U.

10

Il set di spazi dei nomi associati è determinato in base a vari tipi visibili dai tipi di argomento. In particolare, per i modelli di classe gli spazi dei nomi associati includono gli spazi dei nomi associati di tutti gli argomenti del modello. Quando si cercano funzioni non qualificate usando la ricerca dipendente da argomenti, vengono cercati tutti gli spazi dei nomi associati.

Il modello lista degli argomenti di foo<int> è in realtà foo<int, std::allocator<int>>, trascinando così namespace std in scena e c'è già un sovraccarico generale per swap() disponibili da lì.

Problemi correlati