c++ argument-dependent-lookup

c++ - ¿Por qué B:: f no resuelve la ambigüedad pero A:: f lo hace?



argument-dependent-lookup (3)

Cuando el compilador intenta resolver f en f(x) encuentra B::f ya que estamos en el espacio de nombres B También encuentra A::f utilizando la búsqueda dependiente de argumentos, ya que x es una instancia de X que se define en el espacio de nombres A De ahí la ambigüedad.

La declaración que usa B::f no tiene ningún efecto ya que ya estamos en el espacio de nombres B

Para resolver la ambigüedad, utilice A::f(x) o B::f(x) .

¿Por qué B :: f no resuelve la ambigüedad pero A :: f lo hace?

namespace A { class X { }; void f( X ); } namespace B { void f( A::X ); void g( A::X x ) { using B::f; // which expression shall I use here to select B::f? f(x); // ambiguous A::f or B::f } }


Debes escribir espacios de nombres cada vez explícitamente. Solo haz

#include <iostream> namespace A { class X { }; void f( X ) { std::cout << "A"; } } namespace B { void f( A::X ) { std::cout << "B"; } void g( A::X x ) { // using B::f; B::f(x); } } int main() { B::g(A::X()); // outputs B }


Una declaración de uso actúa como una declaración ordinaria: oculta las declaraciones de alcance externo, pero no suprime la búsqueda dependiente de argumentos (ADL).

Cuando using B::f , básicamente no cambias nada. Simplemente vuelve a declarar B::f en el ámbito local, donde ya estaba visible de todos modos. Eso no impide que ADL también encuentre A::f , lo que crea ambigüedad entre A::f y B::f .

Si using A::f , la declaración local de A::f oculta la declaración externa de B::f . Así que B::f ya no es visible y ya no se encuentra por búsqueda de nombre no calificado. Solo se encuentra A::f ahora, lo que significa que ya no hay ambigüedad.

No es posible suprimir ADL. Como el argumento en su caso es de tipo A::X , ADL siempre encontrará la función A::f para el nombre no calificado f . No puedes "excluirlo" de la consideración. Eso significa que no puedes tener en cuenta B::f sin crear ambigüedad. La única manera de evitarlo es usar un nombre calificado.

Como @Richard Smith señaló correctamente en los comentarios, se puede suprimir ADL. ADL solo se usa cuando el nombre de la función se usa como expresión de postfix en la llamada a la función. Especificar la función de destino de cualquier otra manera asustará a la ADL.

Por ejemplo, la inicialización del puntero de función no está sujeta a ADL

void g( A::X x ) { void (*pf)(A::X) = &f; pf(x); }

En el ejemplo anterior se llamará B::f . E incluso un simple par de () alrededor del nombre de la función es suficiente para suprimir ADL, es decir,

void g( A::X x ) { (f)(x); }

Ya es suficiente para hacerlo llamar B::f .