Pregunta

Why doesn't B::f solve the ambiguity but A::f does?

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
    }
}
¿Fue útil?

Solución

A using-declaration acts as an ordinary declaration: it hides outer scope declarations, but it does not suppress argument-dependent lookup (ADL).

When you do using B::f you basically change nothing at all. You simply redeclare B::f in local scope, where it was already visible anyway. That does not prevent ADL from finding A::f as well, which creates ambiguity between A::f and B::f.

If you do using A::f, the local declaration of A::f hides the outer declaration of B::f. So B::f is no longer visible and no longer found by unqualified name lookup. Only A::f is found now, meaning that there's no ambiguity anymore.

It is not possible to suppress ADL. Since the argument in your case is of A::X type, function A::f will always be found by ADL for unqualified name f. You can't "exclude" it from consideration. That means you cannot bring B::f into consideration without creating ambiguity. The only way around is to use a qualified name.

As @Richard Smith correctly noted in the comments, ADL can be suppressed. ADL is only used when the function name itself is used as postfix expression in function call. Specifying the target function in any other way will spook the ADL.

For example, initialization of function pointer is not subject to ADL

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

In the above example B::f will be called. And even a mere pair of () around function name is sufficient to suppress ADL, i.e.

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

is already enough to make it call B::f.

Otros consejos

When the compiler tries to resolve f in f(x) it finds B::f since we are in the namespace B. It also finds A::f using argument dependent lookup since x is an instance of X which is defined in the namespace A. Hence the ambiguity.

The declaration using B::f has no effects since we already are in the namespace B.

To resolve the ambiguity, use A::f(x) or B::f(x).

You should write namespace each time explicitly. Just do

#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
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top