我有两个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)无故障查找不一致。这就是为什么我们有歧义。
<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像往常一样愚蠢,除非我删除"使用std::swap",否则让这一次通过。
还有一点要写:C++重载可能很棘手(C++标准中有30多页),但在appendlix B中有一个可读性很强的10页。。。
感谢所有的良好投入,现在一切都清楚了。
函数调用分几个阶段进行†:
- 名称查找->将候选函数放入所谓的重载集
- ADL的部分,如果您有一个不合格的名称查找
- 模板参数推导->用于重载集中的每个模板
- 过载分辨率->选择最佳匹配
您混淆了第1部分和第3部分。名称查找实际上会将两个swap
函数都放在重载集({N::swap, std::swap}
)中,但第3部分将决定最终调用哪一个。
现在,由于std::swap
是一个模板,并且标准规定在进行过载解析时,非模板函数比模板函数更专业,因此<2>
调用N::swap
:
§13.3.3 [over.match.best] p1
给定这些定义,如果〔…〕,则可行函数
F1
被定义为比另一个可行函数F2
更好的函数
F1
是一个非模板函数,F2
是一个函数模板专门化〔…〕
†我推荐这一优秀系列的前三个视频。
您的测试不检查ADL是否优先于通常的查找,而是检查过载分辨率如何确定最佳匹配。第二个测试用例之所以有效,是因为std::swap
是一个模板,当对(ADL找到的)完美匹配和模板执行重载解析时,非模板化函数优先。