提问人:RoundPi 提问时间:10/5/2012 最后编辑:RoundPi 更新时间:10/6/2012 访问量:1486
为什么 ADL 优先于“std 命名空间”中的函数,但等于用户定义命名空间中的函数?
Why does ADL take precedence over a function in 'std namespace' but is equal to function in user-defined namespace?
问:
我有两个用于演示目的的 ADL 片段。这两个片段都是由VC10,gcc和comeau C++编译器编译的,这三个结果都是相同的。
<1>ADL 反对用户定义命名空间的 using 指令:
#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>ADL 反对 std 命名空间的 using 指令:
#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”被调用。只有当没有“第 1 点”时(比如说,如果我注释掉那一行),编译才会使用回退。N::swap()
std::swap
请注意,这种方式已在许多地方用作覆盖 .
但我的问题是,为什么 ADL 优先于“std 命名空间”(case2),但被认为等于用户定义的命名空间函数(case1)?std::swap
C++标准中有一段是这样说的吗?
================================================================================= 阅读有用的答案后进行编辑,可能对其他人有所帮助。
所以我调整了我的片段 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()
}
我还调整了我的片段 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 &)"
顺便说一句,VC10 像往常一样愚蠢,除非我删除“使用 std::swap”,否则让这个通过。
再写一点:C++重载可能很棘手(C++标准中有30+页),但是在附录B中有一个非常可读的10页......
感谢所有好的输入,现在很清楚了。
答:
函数调用分几个阶段进行†:
- 名称查找 -> 将候选函数放在所谓的重载集中
- 这是 ADL 发生的部分,如果您有一个非限定的名称查找
- 模板参数推导 - 重载集中每个模板的 >
- 过载分辨率 ->选择最佳匹配
您混淆了第 1 部分和第 3 部分。名称查找实际上会将两个函数放在重载集 () 中,但第 3 部分将决定最终调用哪一个函数。swap
{N::swap, std::swap}
现在,由于是一个模板,并且标准说在进行重载解决时,非模板函数比模板函数更专业,因此您的调用:std::swap
<2>
N::swap
§13.3.3 [over.match.best] p1
鉴于这些定义,如果 [...]
F1
F2
F1
是一个非模板函数,是一个函数模板专业化 [...]F2
† 我推荐这个关于该主题的优秀系列的前三个视频。
评论
测试不会检查 ADL 是否优先于常规查找,而是检查重载分辨率如何确定最佳匹配。第二个测试用例起作用的原因是它是一个模板,当对完美匹配(由 ADL 找到)和模板执行重载解决时,非模板化函数优先。std::swap
评论