Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
    }
}
like image 934
Belloc Avatar asked Jul 06 '13 17:07

Belloc


People also ask

Why do ambiguity errors occur?

Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict.

How the ambiguity in multiple inheritance can be resolved?

An ambiguity can arise when several paths exist to a class from the same base class. This means that a child class could have duplicate sets of members inherited from a single base class. This can be solved by using a virtual base class.

How do you resolve ambiguity in hybrid inheritance in C++?

Example: ambiguity in multipath This cause ambiguity in accessing first base class members. To eliminate this problem, C++ has a mechanism to inherit a single copy of properties from the common base class. This is done by declaring the base class as virtual while creating derive classes from this base class.


2 Answers

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.

like image 113
AnT Avatar answered Oct 04 '22 02:10

AnT


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

like image 35
Johan Råde Avatar answered Oct 04 '22 03:10

Johan Råde