Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ADL take precedence over a function in 'std namespace' but is equal to function in user-defined namespace?

I have two snippets for ADL for demo purposes. Both snippets have been compiled by VC10, gcc & comeau C++ compilers, and the result are the same for all three.

<1>ADL against using directive of a user defined namespace:

#include <algorithm>
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap; 
    N::T o1,o2;
    swap(o1,o2); 
}

Compile result:

error C2668: 'M::swap' : ambiguous call to overloaded function
could be 'void M::swap(N::T,N::T)'
or       'void N::swap(N::T,N::T)' [found using argument-dependent lookup]

This is expected as ADL doesn't take precedence over normal lookup result plus ADL is not 2nd class citizen, the ADL search result is unioned with normal(non ADL) unquailfied lookup. That's why we have the ambiguity.

<2>ADL against using directive of std namespace:

#include <algorithm>
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {}  //point 1
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2;
    swap(o1,o2); 
}

This one compiles ok.

The result is compiler choose ADL result(it take precedent of std::swap), meaning N::swap() at 'point 1' will be called. Only when in the absense of 'point 1'(say if I comment out that line), the compile will use the fall back std::swap instead.

Note this way has been used in many places as a way to overwrite the std::swap. But my question is, why does ADL takes precedence over 'std namespace'(case2) but is considered equal to user-defined namespace function(case1)?

Is there a paragraph in C++ standard that says so ?

================================================================================= Edit after reading useful answers, might be helpful to others.

So I have tweaked my snippet 1 & now the ambiguity is gone and compile apparantly prefer Nontemplate function when doing overloading resolution !

#include <algorithm>
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    template<class T>
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap;
    N::T o1,o2;
    swap(o1,o2); //here compiler choose N::swap()
}

I have also tweaked my snippet 2. Just to make the ambiguity appear just for fun !

#include <algorithm>
namespace N 
{ 
    struct T {}; 

    template<class _Ty> inline
    void swap(_Ty& _Left, _Ty& _Right)
    {
        _Ty _Tmp = _Move(_Left);
        _Left = _Move(_Right);
        _Right = _Move(_Tmp);
    }
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2;
    swap(o1,o2); 
}

gcc and comeau both say ambiguity as expected:

"std::swap" matches the argument list, the choices that match are:
            function template "void N::swap(_Ty &, _Ty &)"
            function template "void std::swap(_Tp &, _Tp &)"

BTW VC10 stupid as usual let this one pass ok unless I remove the 'using std::swap'.

Just a bit more to write: C++ overloading can be tricky(30+ page in C++ standard), but at appendlix B of there is a very readable 10 page there...

Thanks for all the nice inputs, now it's clear.

like image 624
RoundPi Avatar asked Oct 05 '12 14:10

RoundPi


People also ask

How does ADL handle namespace-scoped friend functions?

2) namespace-scoped friend functions (and function templates) that are declared in an associated class are visible through ADL even if they are not visible through ordinary lookup 3) all names except for the functions and function templates are ignored (no collision with variables)

What is argument dependent lookup (ADL)?

Argument-dependent lookup, also known as ADL, or Koenig lookup [1], is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators.

Why it is important to write “using namespace std” in C++?

Why it is important to write “using namespace std” in C++ program? In this article, we will discuss the use of “using namespace std” in the C++ program. As the same name can’t be given to multiple variables, functions, classes, etc. in the same scope.

Why can't we declare operators in global or user-defined namespaces?

Name lookup rules make it impractical to declare operators in global or user-defined namespace that operate on types from the std namespace, e.g. a custom operator>> or operator+ for std::vector or for std::pair (unless the element types of the vector/pair are user-defined types, which would add their namespace to ADL).


2 Answers

A function call happens in several stages:

  1. name lookup -> puts candidate functions in a so-called overload set
    • this is the part where ADL happens if you have an unqualified name lookup
  2. template argument deduction -> for every template in the overload set
  3. overload resolution -> pick the best match

You're confusing part 1 with part 3. The name lookup will actually put both swap functions in the overload set ({N::swap, std::swap}), but part 3 will decide, which one to call in the end.

Now, since std::swap is a template, and the standard says that non-template functions are more specialized than template functions when doing overload resolution, your <2> calls N::swap:

§13.3.3 [over.match.best] p1

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [...]

  • F1 is a non-template function and F2 is a function template specialization [...]

† I recommend the first three videos of this excellent series on the subject.

like image 104
Xeo Avatar answered Sep 26 '22 21:09

Xeo


Your test does not check whether ADL takes precedence or not over usual lookup, but rather how overload resolution determines the best match. The reason that the second test case works is that std::swap is a template, and when performing overload resolution on a perfect match (found by ADL) and a template, the non-templated function takes precedence.

like image 44
David Rodríguez - dribeas Avatar answered Sep 22 '22 21:09

David Rodríguez - dribeas