【问题标题】:Why does ADL take precedence over a function in 'std namespace' but is equal to function in user-defined namespace?为什么 ADL 优先于“std 命名空间”中的函数但等于用户定义命名空间中的函数?
【发布时间】:2012-09-26 17:24:49
【问题描述】:

我有两个用于 ADL 的 sn-ps 用于演示目的。两个 sn-ps 都被 VC10、gcc 和 comeau C++ 编译器编译过,结果都是一样的。

针对用户定义命名空间的 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)非限定查找联合。这就是我们有歧义的原因。

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

这个编译好了。

结果是编译器选择 ADL 结果(它优先于 std::swap),这意味着将调用 'point 1' 处的 N::swap()。只有在没有“第 1 点”的情况下(比如我注释掉那行),编译才会使用回退 std::swap 来代替。

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

C++ 标准中有一段这样说吗?

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

所以我已经调整了我的 sn-p 1 并且现在歧义已经消失并且在进行重载解析时编译显然更喜欢非模板函数!

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

我还调整了我的 sn-p 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 &)"

BTW VC10 像往常一样愚蠢,除非我删除“使用 std::swap”,否则让这个通过就好了。

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

感谢所有精彩的输入,现在很清楚了。

【问题讨论】:

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


    【解决方案1】:

    函数调用发生在几个阶段

    1. 名称查找 -> 将候选函数放入所谓的 重载集
      • 如果您有不合格的名称查找,这就是 ADL 发生的部分
    2. 模板参数推导 -> 重载集中的每个模板
    3. 重载分辨率 -> 选择最佳匹配

    您将第 1 部分和第 3 部分混淆了。名称查找实际上会将两个 swap 函数放入重载集 ({N::swap, std::swap}),但第 3 部分将决定最后调用哪个函数。

    现在,由于std::swap 是一个模板,并且标准规定在进行重载解析时非模板函数比模板函数更专业,所以您的&lt;2&gt; 调用N::swap

    §13.3.3 [over.match.best] p1

    鉴于这些定义,一个可行函数 F1 被定义为比另一个可行函数 F2 更好的函数,如果 [...]

    • F1 是非模板函数,F2 是函数模板特化 [...]

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

    【讨论】:

    • 感谢标准参考!
    • @Xeo:不知道(关于否决票),第一句话让我有点困惑,因为编译器没有找到可能被调用的 all 函数。名称查找只是找到一个子集。
    • @Matthieu:我有点过分概括了。修复和改进。
    【解决方案2】:

    您的测试不会检查 ADL 是否优先于通常的查找,而是检查重载解决方案如何确定最佳匹配。第二个测试用例起作用的原因是std::swap是一个模板,当对完全匹配(由ADL发现)和模板执行重载解析时,非模板函数优先。

    【讨论】:

    • 值得注意的是ADL属于name lookup,name lookup没有“优先级”的概念。
    • @KerrekSB:我认为 David 是在名称查找阶段之后在重载解析时谈论的,这就是选择最佳匹配。
    猜你喜欢
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-28
    • 1970-01-01
    • 2012-01-20
    相关资源
    最近更新 更多