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

时间:2012-10-05 14:15:27

标签: c++ namespaces using-directives argument-dependent-lookup

我有两个用于演示目的的ADL片段。这两个片段都是由VC10编写的,gcc& comeau C ++编译器,结果对于这三个编译器都是一样的。

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

编译结果:

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)无法定义查找联合。这就是为什么我们有歧义。

&lt; 2&gt; 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); 
}

这个编译好了。

结果是编译器选择ADL结果(它采用std :: swap的先例),意味着将调用'point 1'处的N::swap()。只有当缺少“第1点”时(比如我注释掉那一行),编译才会使用后退std::swap

请注意,这种方式已在许多地方用作覆盖std::swap的方法。 但我的问题是,为什么ADL优先于'std namespace'(case2)但被认为等于用户定义的命名空间函数(case1)?

C ++标准中是否有段落这样说?

=============================================== ================================== 阅读有用的答案后编辑,可能对其他人有帮助。

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

我还调整了我的片段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和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 &)"
除非我删除'using std :: swap',否则BTW VC10像往常一样愚蠢,让这个通过ok。

写一点:C ++重载可能很棘手(C ++标准中有30多页),但在appendlix B上有一个非常易读的10页......

感谢所有不错的输入,现在很清楚。

2 个答案:

答案 0 :(得分:11)

函数调用分几个阶段发生

  1. 名称查询 - &gt;将候选函数放在所谓的重载集
    • 如果您有不合格的名称查找,则这是ADL发生的部分
  2. 模板参数推断 - &gt;对于过载集中的每个模板
  3. 重载分辨率 - &gt;选择最佳匹配
  4. 你将第1部分与第3部分混淆。名称查找实际上将两个swap函数放在重载集({N::swap, std::swap})中,但是第3部分将决定在哪一个调用端。

    现在,由于std::swap是一个模板,并且标准表示非模板函数在进行重载解析时比模板函数更专业,因此<2>调用{ {1}}:

    N::swap

      

    鉴于这些定义,如果[...]

    ,可行函数§13.3.3 [over.match.best] p1被定义为比另一个可行函数F1更好的函数。      
        
    • F2是非模板函数,F1是函数模板特化[...]
    •   

    †我推荐有关this excellent series的前三个视频。

答案 1 :(得分:9)

您的测试不会检查ADL是否优先于常规查找,而是检查重载决策如何确定最佳匹配。第二个测试用例的工作原因是std::swap是一个模板,当对完美匹配(由ADL找到)和模板执行重载决策时,非模板化函数优先。