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
}
}
Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict.
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.
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.
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
.
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)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With