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

StackOverflow https://stackoverflow.com/questions/12748212

Pregunta

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.

¿Fue útil?

Solución

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.

Otros consejos

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top