En el lenguaje de programación C++ , la búsqueda dependiente de argumentos ( ADL ), o búsqueda de nombre dependiente de argumentos , [1] se aplica a la búsqueda de un nombre de función no calificado según los tipos de argumentos proporcionados a la llamada de función . Este comportamiento también se conoce como búsqueda de Koenig , ya que a menudo se atribuye a Andrew Koenig , aunque él no es su inventor. [2]
Durante la búsqueda dependiente de argumentos, se pueden buscar otros espacios de nombres que no se consideran durante la búsqueda normal, donde el conjunto de espacios de nombres que se buscarán depende de los tipos de argumentos de la función. Específicamente, el conjunto de declaraciones descubiertas durante el proceso ADL, y consideradas para la resolución del nombre de la función, es la unión de las declaraciones encontradas por la búsqueda normal con las declaraciones encontradas al buscar en el conjunto de espacios de nombres asociados con los tipos de argumentos de la función.
Un ejemplo de ADL se ve así:
espacio de nombres NS { clase A {}; vacío f ( A & a , int i ) {} } // espacio de nombres NS int main () { NS :: A a ; f ( a , 0 ); // Llama a NS::f. }
A pesar de que elprincipalLa función no está en el espacio de nombres NS, ni el espacio de nombres NS está en el alcance, la funciónNS::f(A&, entero)se encuentra debido a los tipos declarados de los argumentos reales en la declaración de llamada de función.
Un patrón común en la biblioteca estándar de C++ es declarar operadores sobrecargados que se encontrarán de esta manera. Por ejemplo, este sencillo programa Hola mundo no se compilaría si no fuera por ADL:
#include <iostream> #include <cadena> int main () { std :: string str = "hola mundo" ; std :: cout << str ; }
Usar <<
equivale a llamar operator<<
sin el std::
calificador. Sin embargo, en este caso, la sobrecarga del operador << que funciona para string
está en el std
espacio de nombres, por lo que se requiere ADL para su uso.
El siguiente código funcionaría sin ADL (que se le aplica de todos modos):
#include <flujo de datos> int principal () { std :: cout << 5 ; }
Funciona porque el operador de salida para números enteros es una función miembro de la std::ostream
clase, que es del tipo cout
. Por lo tanto, el compilador interpreta esta declaración como
std :: cout.operador << ( 5 ) ;
que puede resolverse durante una búsqueda normal. Sin embargo, tenga en cuenta que, por ejemplo, la función const char *
sobrecargada operator<<
es una función que no es miembro del std
espacio de nombres y, por lo tanto, requiere ADL para una búsqueda correcta:
/* imprimirá la cadena de caracteres proporcionada como se esperaba utilizando ADL derivado del tipo de argumento std::cout */ operador << ( std :: cout , "Hola" ) /* llama a una función miembro ostream del operador<< tomando una constante void*, que imprimirá la dirección de la cadena de caracteres proporcionada en lugar del contenido de la cadena de caracteres */ std :: cout . operator << ( "Hola" )
La función std
no miembro sobrecargada del espacio de nombres operator<<
para manejar cadenas es otro ejemplo:
/*equivalente al operador<<(std::cout, str). El compilador busca el espacio de nombres std utilizando ADL debido al tipo std::string del parámetro str y std::cout */ std :: cout << str ;
Como señala Koenig en una nota personal, [2] sin ADL el compilador indicaría un error indicando que no pudo encontrarlo operator<<
, ya que la declaración no especifica explícitamente que se encuentra en el std
espacio de nombres.
Las funciones encontradas por ADL se consideran parte de la interfaz de una clase. En la biblioteca estándar de C++, varios algoritmos utilizan llamadas no calificadas a swap
from dentro del std
espacio de nombres. Como resultado, std::swap
se utiliza la función genérica si no se encuentra nada más, pero si estos algoritmos se utilizan con una clase de terceros, Foo
, que se encuentra en otro espacio de nombres que también contiene , se utilizará swap(Foo&, Foo&)
esa sobrecarga de .swap
Si bien ADL hace que sea práctico que las funciones definidas fuera de una clase se comporten como si fueran parte de la interfaz de esa clase, hace que los espacios de nombres sean menos estrictos y, por lo tanto, pueden requerir el uso de nombres completamente calificados cuando, de lo contrario, no serían necesarios. Por ejemplo, la biblioteca estándar de C++ hace un uso extensivo de llamadas no calificadas a std::swap
para intercambiar dos valores. La idea es que, luego, uno puede definir una versión propia de swap
en su propio espacio de nombres y se usará dentro de los algoritmos de la biblioteca estándar. En otras palabras, el comportamiento de
espacio de nombres N { estructura A {}; } // espacio de nombres N Una a ; Una b ; std :: swap ( a , b );
Puede o no ser el mismo que el comportamiento de
usando std :: swap ; swap ( a , b );
(donde a
y b
son de tipo N::A
) porque si N::swap(N::A&, N::A&)
existe, el segundo de los ejemplos anteriores lo llamará mientras que el primero no. Además, si por alguna razón se definen tanto N::swap(N::A&, N::A&)
como , entonces el primer ejemplo llamará pero el segundo no se compilará porque sería ambiguo.std::swap(N::A&, N::A&)
std::swap(N::A&, N::A&)
swap(a, b)
En general, la dependencia excesiva de ADL puede generar problemas semánticosL1
. Si una biblioteca, , espera que las llamadas no calificadas a foo(T)
tengan un significado y otra biblioteca, L2
espera que tenga otro, entonces los espacios de nombres pierden su utilidad. Sin embargo, si , L1
espera L1::foo(T)
tener un significado y L2
hace lo mismo, entonces no hay conflicto, pero las llamadas a foo(T)
tendrían que estar completamente calificadas (es decir, L1::foo(x)
en contraposición a using L1::foo; foo(x);
) para que ADL no interfiera.