Search code examples
c++argument-dependent-lookupname-lookup

ADL name lookup problem, is using std::swap; swap(a,b) related to function overloading or inner scope function hide outer scope function?


I know what ADL is and I know in C++, inner scope function hides outer scope functions. That is, names do not overload across scopes. So funtion overloading need to be done in the same scope.

So now my question is, for this common code snippet:

#include <iostream>
#include <string>
using std::cout;
using std::endl;

namespace Foo{
    class Bar{
        friend void swap(Bar& a, Bar& b);
    };
    void swap(Bar& a, Bar& b){
        cout << "I am here" << endl;
    }
}

int main(int argc, char *args[]){
    Foo::Bar a, b;
    using std::swap; //These 2 lines
    swap(a, b);      //output is "I am here", Foo::swap is called
}

Along with ADL, is:

  1. custom swap and std::swap are both visiable and considered as overloaded, and then choose the best match?

  2. custom swap is found first then name lookup stops (std::swap is hidden)?


If 1. is true, how does it work? Function overloading over 2 different scopes? This contradicts what I write at the beginning. using std::swap introduce std::swap to current scope. And custom swap is in Foo::swap.

Btw, this answer What is “Argument-Dependent Lookup” (aka ADL, or “Koenig Lookup”)? seems to indicate that they are function overloading.

Further, if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined, then the first example will call std::swap(A::MyClass&, A::MyClass&) but the second will not compile because swap(obj1, obj2) would be ambiguous.

If it's function overloading, why does my swap(Bar& a, Bar& b) not has the ambiguity issue as he describes? Is he wrong?


if 2. is true, according to C++ Primer 5th 18.2.3:

std::cin >> s;

is equivalent to:

operator>>(std::cin, s);

In this example, when the compiler sees the “call” to operator>>, it looks for a matching function in the current scope, including the scopes enclosing the output statement. In addition, because the >> expression has parameters of class type, the compiler also looks in the namespace(s) in which the types of cin and s are defined. Thus, for this call, the compiler looks in the std namespace, which defines the istream and string types. When it searches std, the compiler finds the string output operator function.

So the name lookup order is: current scope --> enclosing scopes --> argument namespace scope.

Then why is not std::swap hiding custom swap? using std::swap introduces std::swap to the current scope, which has the higher lookup priority.


Both of my assumptions seems to be invalid in some pionts and I am getting confused. Need some help. Thanks in advance.


Solution

  • 1. is true. For ADL,

    These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.

    std::swap is found by usual unqualified name lookup, and Foo::swap is found by ADL, they're both in the overload set. Foo::swap is a non-template function which is perferred to std::swap which is a template in overload resolution.

    F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and

    ...

    4) or, if not that, F1 is a non-template function while F2 is a template specialization

    And about the quote from the linked answer,

    if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined,

    There's no std::swap(A::MyClass&, A::MyClass&) in fact, it's just an assumption, if there's a non-template std::swap(A::MyClass&, A::MyClass&) then the invocation would be ambiguous ...