简体   繁体   English

为什么ADL优先于'std namespace'中的函数,但是等于用户定义的命名空间中的函数?

[英]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. 我有两个用于ADL的片段用于演示目的。 Both snippets have been compiled by VC10, gcc & comeau C++ compilers, and the result are the same for all three. 这两个片段都是由VC10,gcc和comeau C ++编译器编译的,结果对于这三个片段都是相同的。

<1>ADL against using directive of a user defined namespace: <1> 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); 
}

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. 这是预期的,因为ADL不优先于正常查找结果加上ADL不是二等公民,ADL搜索结果与正常(非ADL)无法定义查找联合。 That's why we have the ambiguity. 这就是为什么我们有歧义。

<2>ADL against using directive of std namespace: <2> ADL反对使用std命名空间的指令:

#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. 结果是编译器选择ADL结果(它采用std :: swap的先例),意味着将调用'point 1'处的N::swap() 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. 只有当缺少'第1点'时(比如我注释掉那一行),编译才会使用后退std::swap代替。

Note this way has been used in many places as a way to overwrite the std::swap . 注意这种方式已经在许多地方用作覆盖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)? 但我的问题是,为什么ADL优先于'std namespace'(case2)但被认为等于用户定义的命名空间函数(case1)?

Is there a paragraph in C++ standard that says so ? C ++标准中是否有段落这样说?

================================================================================= 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 ! 所以我已经调整了我的代码片段1,现在模糊性已经消失,并且在进行重载分辨率时编译显然更喜欢Nontemplate函数!

#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 ! 我也调整了我的片段2.只是为了让模糊看起来很有趣!

#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: gcc和comeau都表达了预期的含糊不清:

"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'. BTW VC10像往常一样愚蠢,让这个通过ok,除非我删除'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... 写一点:C ++重载可能很棘手(C ++标准中有30多页),但在appendlix B中有一个非常易读的10页...

Thanks for all the nice inputs, now it's clear. 感谢所有不错的输入,现在很清楚。

A function call happens in several stages : 函数调用分几个阶段进行

  1. name lookup -> puts candidate functions in a so-called overload set name lookup - >将候选函数放入所谓的重载集中
    • this is the part where ADL happens if you have an unqualified name lookup 如果您具有非限定名称查找,则这是ADL发生的部分
  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. 您将第1部分与第3部分混淆。名称查找实际上将两个swap函数放在重载集( {N::swap, std::swap} )中,但第3部分将决定,最后调用哪一个。

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 : 现在,因为std::swap是一个模板,并且标准说在进行重载解析时非模板函数比模板函数更专业 ,所以<2>调用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被定义为比另一个可行函数F2更好的函数,如果[...]

  • F1 is a non-template function and F2 is a function template specialization [...] F1是非模板功能, F2是功能模板专业化[...]

† I recommend the first three videos of this excellent series on the subject. †我推荐这个优秀系列的前三个视频。

Your test does not check whether ADL takes precedence or not over usual lookup, but rather how overload resolution determines the best match. 您的测试不会检查ADL是否优先于通常的查找,而是检查重载决策如何确定最佳匹配。 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. 第二个测试用例的工作原因是std::swap是一个模板,当对完美匹配(由ADL找到)和模板执行重载解析时,非模板化函数优先。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM