19

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
    }
}
Bojangles
  • 99,427
  • 50
  • 170
  • 208
Belloc
  • 6,318
  • 3
  • 22
  • 52

3 Answers3

21

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
10

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

Community
  • 1
  • 1
Johan Råde
  • 20,480
  • 21
  • 73
  • 110
1

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
}
sasha.sochka
  • 14,395
  • 10
  • 44
  • 68
  • yes: ... and never do `using namespace xxx` - it only obscures and makes for nasty bugs (not even `using namespace std` :) – slashmais Jul 13 '13 at 16:44
  • @slashmais, of course that's true. But in such a small snippets I think it's allowed. The most important is not to get used to it - if you do you will forget not to write like this in real code. And yes - fixed! – sasha.sochka Jul 13 '13 at 16:52