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
.