c++ namespaces argument-dependent-lookup name-lookup class-template

c++ - ¿Por qué esta llamada a swap() es ambigua?



namespaces argument-dependent-lookup (2)

El siguiente programa

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

hace que g++ y clang emitan el siguiente error de compilación en mi 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) ^

No entiendo por qué std::swap está siendo considerado como una sobrecarga candidata, pero tiene algo que ver con el uso de std::allocator<T> por parte de foo .

Eliminar el segundo parámetro de la plantilla de foo permite que el programa se compile sin errores.


Debido a que std::allocator<T> se usa como un argumento de tipo de plantilla, el std nombres std es un espacio de nombres asociado para ADL.

[basic.lookup.argdep]/2 , viñeta 2, énfasis mío:

Además, si T es una especialización de plantilla de clase, sus espacios de nombres y clases asociados también incluyen: los espacios de nombres y clases asociados con los tipos de argumentos de plantilla proporcionados para los parámetros de tipo de plantilla (excluyendo los parámetros de plantilla de plantilla); los espacios de nombres de los cuales cualquier argumento de plantilla de plantilla son miembros; y las clases de las cuales todas las plantillas miembro utilizadas como argumentos de plantilla de plantilla son miembros.

... y los punteros tienen el mismo conjunto de espacios de nombres / clases asociados que el tipo al que apuntan:

Si T es un puntero a U o una matriz de U , sus espacios de nombres y clases asociados son aquellos asociados con U


El conjunto de espacios de nombres asociados se determina en función de varios tipos visibles desde los tipos de argumentos. En particular, para las plantillas de clase, los espacios de nombres asociados incluyen los espacios de nombres asociados de todos los argumentos de la plantilla. Al buscar funciones no calificadas utilizando la búsqueda dependiente del argumento, se buscan todos los espacios de nombres asociados.

La lista de argumentos de la plantilla de foo<int> es en realidad foo<int, std::allocator<int>> , arrastrando así el espacio de nombres std a la imagen y ya existe una sobrecarga general para swap() disponible desde allí.