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 aU
o una matriz deU
, sus espacios de nombres y clases asociados son aquellos asociados conU
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í.