Argument-dependent lookup
La búsqueda dependiente de argumentos (ADL), también conocida como búsqueda de Koenig [1] , es el conjunto de reglas para buscar nombres de funciones no calificados en expresiones de llamada a funciones , incluyendo llamadas implícitas a funciones para operadores sobrecargados . Estos nombres de función se buscan en los espacios de nombres de sus argumentos además de los ámbitos y espacios de nombres considerados por la habitual búsqueda de nombres no calificados .
La búsqueda dependiente de argumentos hace posible utilizar operadores definidos en un espacio de nombres diferente. Ejemplo:
#include <iostream> int main() { std::cout << "Test\n"; // No hay operator<< en el espacio de nombres global, pero ADL // examina el espacio de nombres std porque el argumento izquierdo está en // std y encuentra std::operator<<(std::ostream&, const char*) operator<<(std::cout, "Test\n"); // Lo mismo, usando notación de llamada a función // Sin embargo, std::cout << endl; // Error: “endl” no está declarado en este espacio de nombres. // Esto no es una llamada a función endl(), por lo que ADL no aplica endl(std::cout); // OK: esto es una llamada a función: ADL examina el espacio de nombres std // porque el argumento de endl está en std, y encuentra std::endl (endl)(std::cout); // Error: “endl” no está declarado en este espacio de nombres. // La sub-expresión (endl) no es un unqualified-id }
Contenidos |
Detalles
Primero, la búsqueda dependiente de argumento no se considera si el conjunto de búsqueda producido por la búsqueda no calificada usual contiene cualquiera de los siguientes:
De lo contrario, para cada argumento en una expresión de llamada de función se examina su tipo para determinar el conjunto asociado de espacios de nombres y clases que añadirá a la búsqueda.
T
o puntero a un arreglo de
T
, el tipo
T
es examinado y su conjunto asociado de clases y espacios de nombres es añadido al conjunto.
F
de la clase
X
, los tipos de parámetros de la función, el tipo de retorno de la función, y la clase
X
son examinados y sus conjuntos asociados de clases y espacios de nombres son añadidos al conjunto.
T
de la clase
X
, tanto el tipo miembro como el tipo
X
son examinados y su conjunto asociado de clases y espacios de nombres se añade al conjunto.
- Adicionalmente, si el conjunto de sobrecargas es nombrado por un identificador de plantilla , todos sus argumentos de plantilla de tipo y argumentos de plantilla de plantilla (pero no argumentos de plantilla constantes) son examinados y su conjunto asociado de clases y espacios de nombres son añadidos al conjunto.
|
Si cualquier espacio de nombres en el conjunto asociado de clases y espacios de nombres es un espacio de nombres en línea , su espacio de nombres contenedor también se añade al conjunto. Si cualquier espacio de nombres en el conjunto asociado de clases y espacios de nombres contiene directamente un espacio de nombres en línea, ese espacio de nombres en línea se añade al conjunto. |
(desde C++11) |
Después de que se determina el conjunto asociado de clases y espacios de nombres, todas las declaraciones encontradas en las clases de este conjunto se descartan para el propósito del procesamiento adicional de ADL, excepto las funciones friend y plantillas de función con ámbito de espacio de nombres, como se establece en el punto 2 a continuación.
El conjunto de declaraciones encontradas mediante la búsqueda no calificada ordinaria unqualified lookup y el conjunto de declaraciones encontradas en todos los elementos del conjunto asociado producido por ADL, se combinan, con las siguientes reglas especiales:
Notas
Debido a la búsqueda dependiente de argumentos, las funciones no miembro y los operadores no miembro definidos en el mismo espacio de nombres que una clase se consideran parte de la interfaz pública de esa clase (si se encuentran mediante ADL) [2] .
ADL es la razón detrás del patrón establecido para intercambiar dos objetos en código genérico:
using
std::
swap
;
swap
(
obj1, obj2
)
;
porque llamar directamente
std::
swap
(
obj1, obj2
)
no consideraría las funciones
swap()
definidas por el usuario que podrían estar definidas en el mismo espacio de nombres que los tipos de
obj1
o
obj2
, y simplemente llamar
swap
(
obj1, obj2
)
no llamaría a nada si no se proporcionara ninguna sobrecarga definida por el usuario. En particular,
std::iter_swap
y todos los demás algoritmos de la biblioteca estándar utilizan este enfoque cuando tratan con tipos
Swappable
.
Las reglas de búsqueda de nombres hacen que sea poco práctico declarar operadores en el espacio de nombres global o definido por el usuario que operen en tipos del espacio de nombres
std
, por ejemplo, un
operator
>>
o
operator
+
personalizado para
std::vector
o para
std::pair
(a menos que los tipos de elemento del vector/par sean tipos definidos por el usuario, lo que agregaría su espacio de nombres a ADL). Dichos operadores no serían encontrados en las instanciaciones de plantillas, como los algoritmos de la biblioteca estándar. Consulte
nombres dependientes
para más detalles.
ADL puede encontrar una friend function (típicamente, un operador sobrecargado) que está definida completamente dentro de una clase o plantilla de clase, incluso si nunca fue declarada a nivel de namespace.
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Definición dentro de // una plantilla de clase }; // A menos que se proporcione una declaración coincidente, gcd es // un miembro invisible (excepto mediante ADL) de este espacio de nombres void g() { number<double> a(3), b(4); a = gcd(a, b); // Encuentra gcd porque number<double> es una clase asociada, // haciendo gcd visible en su espacio de nombres (ámbito global) // b = gcd(3, 4); // Error; gcd no es visible }
|
Aunque una llamada a función puede resolverse mediante ADL incluso si la búsqueda ordinaria no encuentra nada, una llamada a función a una plantilla de función con argumentos de plantilla explícitamente especificados requiere que haya una declaración de la plantilla encontrada por búsqueda ordinaria (de lo contrario, es un error de sintaxis encontrar un nombre desconocido seguido de un carácter menor-que). namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error until C++20 (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a constant parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f } |
(hasta C++20) |
En los siguientes contextos se produce una búsqueda solo por ADL (es decir, búsqueda únicamente en espacios de nombres asociados):
|
(desde C++11) |
- la búsqueda de nombres dependientes desde el punto de instanciación de la plantilla.
|
(desde C++17) |
Ejemplos
|
Esta sección está incompleta
Razón: más ejemplos |
Ejemplo de http://www.gotw.ca/gotw/030.htm
namespace A { struct X; struct Y; void f(int); void g(X); } namespace B { void f(int i) { f(i); // Llama a B::f (recursión infinita) } void g(A::X x) { g(x); // Error: ambigüedad entre B::g (búsqueda ordinaria) // y A::g (búsqueda dependiente de argumento) } void h(A::Y y) { h(y); // Llama a B::h (recursión infinita): ADL examina el espacio de nombres A // pero no encuentra A::h, por lo que solo se usa B::h de la búsqueda ordinaria } }
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 33 | C++98 |
los espacios de nombres o clases asociados no estaban especificados
si un argumento utilizado para la búsqueda es la dirección de un grupo de funciones sobrecargadas o una plantilla de función |
especificado |
| CWG 90 | C++98 |
las clases asociadas de una clase anidada no-unión
no incluían su clase envolvente, pero una unión anidada estaba asociada con su clase envolvente |
las no-uniones también asociadas |
| CWG 239 | C++98 |
una declaración de función en ámbito de bloque encontrada en la
búsqueda no calificada ordinaria no prevenía que ocurriera ADL |
ADL no se considera excepto
para declaraciones using |
| CWG 997 | C++98 |
los tipos de parámetros dependientes y tipos de retorno estaban
excluidos de la consideración al determinar las clases y espacios de nombres asociados de una plantilla de función |
incluidos |
| CWG 1690 |
C++98
C++11 |
ADL no podía encontrar lambdas (C++11) u objetos
de tipos de clase local (C++98) que son retornados |
pueden ser encontrados |
| CWG 1691 | C++11 | ADL tenía comportamientos sorprendentes para declaraciones opacas de enumeración | corregido |
| CWG 1692 | C++98 |
las clases doblemente anidadas no tenían espacios de nombres asociados
(sus clases envolventes no son miembros de ningún espacio de nombres) |
los espacios de nombres asociados son
extendidos a los espacios de nombres envolventes más internos |
| CWG 2857 | C++98 |
las clases asociadas de un tipo de clase
incompleto incluían sus clases base |
no incluidas |
Véase también
Enlaces externos
|