6

我有两个用于演示目的的 ADL 片段。两个片段都已由 VC10、gcc 和 comeau C++ 编译器编译,并且所有三个的结果都是相同的。

<1>针对用户定义命名空间的 using 指令的 ADL:

#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); 
}

编译结果:

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]

这是预期的,因为 ADL 不优先于正常查找结果加上 ADL 不是二等公民,ADL 搜索结果与正常(非 ADL)未限定查找联合。这就是为什么我们有歧义。

<2>针对 std 命名空间的 using 指令的 ADL:

#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); 
}

这个编译好了。

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.

4

2 回答 2

11

A function call happens in several stages†</sup>:

  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.

于 2012-10-05T14:20:07.710 回答
10

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.

于 2012-10-05T14:23:59.933 回答