23

Das folgende ProgrammWarum ist dieser Aufruf zu swap() mehrdeutig?

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

beide g++ verursacht und clang den folgenden Compiler-Fehler auf meinem System zu erteilen:

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

Ich verstehe nicht, warum std::swap als Kandidat Überlastung betrachtet wird, aber Es hat etwas mit foo Verwendung von std::allocator<T> zu tun.

Durch Eliminierung des zweiten Vorlagenparameters foo kann das Programm ohne Fehler kompiliert werden.

Antwort

17

Da std::allocator<T> als Schablonentypargument verwendet wird, ist der Namespace std ein zugehöriger Namespace für ADL.

[basic.lookup.argdep]/2 Punkt 2, Hervorhebung von mir:

Wenn darüber hinaus T eine Klasse Template-Spezialisierung ist, sind seine zugehörigen Namespaces und Klassen auch: die Namespaces und Klassen mit den Typen der Vorlage zugeordnet Argumente, die für Vorlagentyp-Parameter bereitgestellt werden (ohne Template-Vorlagenparameter); die Namespaces, für die Vorlagenvorlagenargumente Mitglieder sind; und die Klassen, von denen alle Elementvorlagen, die als Vorlage Vorlagenargumente verwendet werden, Mitglieder sind.

... und Zeiger haben den gleichen Satz von zugehörigen Namensräume/Klassen als Typ sie zeigen:

Wenn T ist ein Zeiger auf U oder ein Array von U, seine zugehörigen Namensräume und Klassen sind die mit U verbundenen Klassen.

10

Die Menge der zugeordneten Namespaces wird anhand verschiedener Typen ermittelt, die aus den Argumenttypen sichtbar sind. Bei Klassenvorlagen enthalten die zugeordneten Namespaces die zugehörigen Namespaces aller Vorlagenargumente. Beim Suchen nach unqualifizierten Funktionen, die eine argumentabhängige Suche verwenden, werden alle zugehörigen Namespaces durchsucht.

Die Template-Argument Liste der foo<int> ist eigentlich foo<int, std::allocator<int>>, wodurch Namespace ziehen std in das Bild, und es gibt bereits eine allgemeine Überlast für swap() von dort zur Verfügung.