Search code examples
c++argument-dependent-lookup

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


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
    }
}

Solution

  • 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.