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

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

我有两个 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)非限定查找 union 。这就是我们存在歧义的原因。

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

这个编译没问题。

结果是编译器选择 ADL 结果(它采用 std::swap 的先例),这意味着将调用“点 1”处的 N::swap()。只有在缺少“第 1 点”的情况下(比如我注释掉该行),编译才会使用后备 std::swap 代替。

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

C++标准中有这么一段话吗?

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

所以我调整了我的代码片段 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()
}

我还调整了我的代码片段 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 像往常一样愚蠢让这个通过 ok 除非我删除'using std::swap'。

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

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

最佳答案

一个函数调用发生在几个阶段:

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

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

现在,自 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 is a non-template function and F2 is a function template specialization [...]

† 我推荐this excellent series的前三个视频关于这个问题。

关于c++ - 为什么 ADL 优先于 'std namespace' 中的函数,但等于用户定义的命名空间中的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12748212/

相关文章:

python - 在 Python 函数中,有没有办法在调用方告诉参数的变量名称?

ASP.NET:尝试解析 Google Base XML,无法访问 "g:"标签

c++ - 为什么公共(public)重载与某些编译器上的私有(private) using 指令冲突?

c++ - 如何检测集合中的当前元素是否是最后一个元素?

c++ - 使用 return String1 - String2 == 0 进行相等性检查是否安全?

c++ - Visual Studio 2015 D4024 & LNK1181

c++ - 线程连接问题

c++ - 命名空间内的友元函数声明/定义

c++ - : "using namespace"?的目的是什么

ms-access - USING 语句的 VBA 是什么