c++ ordinary lookup vs argument dependent lookup

Viewed 545

Considering this sample described in http://en.cppreference.com/w/cpp/language/adl:

namespace A {
      struct X;
      struct Y;
      void f(int);
      void g(X);
}

namespace B {
    void f(int i) {
        f(i);   // calls B::f (endless recursion)
    }
    void g(A::X x) {
        g(x);   // Error: ambiguous between B::g (ordinary lookup)
                //        and A::g (argument-dependent lookup)
    }
    void h(A::Y y) {
        h(y);   // calls B::h (endless recursion): ADL examines the A namespace
                // but finds no A::h, so only B::h from ordinary lookup is used
    }
}

I am wondering why the ambiguity appears since the ADL rules are not taken into account if

"the lookup set produced by usual unqualified lookup contains any of the following".

Here B::g can be found by unqualified lookup as explained in http://en.cppreference.com/w/cpp/language/unqualified_lookup thanks to the rule

For a name used in the definition of a function, either in its body or as part of default argument, where the function is a member of user-declared or global namespace, the block in which the name is used is searched before the use of the name, then the enclosing block is searched before the start of that block, etc, until reaching the block that is the function body. Then the namespace in which the function is declared is searched until the definition (not necessarily the declaration) of the function that uses the name, then the enclosing namespaces, etc.

Then my question is why are ADL rules considered in this case?

3 Answers

Thanks for this question. I came here looking for the answer myself, and I think I have been able to come up with an example which falls under this rule

the argument-dependent lookup is not considered if the lookup set produced by usual unqualified lookup contains any of the following:

  • ...

  • a declaration of a function at block scope (that's not a using-declaration)

and switches off ADL indeed.

#include <iostream>

namespace x {
    struct type {};
    void fn(type) { std::puts("ADL"); }
}

int main() {
    // Forward function declaration within the function scope.
    // This way "Regular" gets printed out.
    // Comment this declaration out and you'll get "ADL".
    void fn(x::type);

    fn(x::type{});
    return 0;
}
void fn(x::type) { std::puts("Regular"); }

If you put this forward declaration before the main, you'll get an ambiguous call as both the usual unqualified name lookup and ADL would kick in. But having declared the function within the function scope, everything works as promised.

Still not sure how often one can find this in a wild. At least, I have not seen anything like that yet and can hardly imagine where such a coding trick would be beneficial.

Related