这是问题的后续here https://stackoverflow.com/questions/46782156/does-argument-dependent-lookup-go-before-normal-scope-lookup使用参数相关查找 (ADL) 进行函数重载。我想检查我对这些情况下规则的理解,所以我编写了一些测试代码。
首先,当然,std 中的 HasPtr 类没有交换,因此我编写了自己的命名空间,其中除了已在全局范围中定义的交换之外,还包含 HasPtr 版本的交换。 using 声明按照我的预期工作——产生了歧义错误,因为已经定义了一个 HasPtr 版本的 swap,如“C++ Primer”第 5 章中所做的那样。
然后我想看看如果我将 using 声明更改为 using 指令会发生什么。书上说编译器将保持沉默,直到函数被实际调用。我想验证一下,所以代码如下:
#include <string>
class HasPtr {
public:
friend void swap(HasPtr&, HasPtr&);
std::string *ps;
};
void swap(HasPtr &lhs, HasPtr &rhs) {
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
namespace myNS {
void swap(HasPtr &lhs, HasPtr &rhs) {
std::string s = "in my name space";
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
}
class Foo {
friend void swap(Foo &lhs, Foo &rhs);
HasPtr h;
};
void swap(Foo &lhs, Foo &rhs) {
using std::swap; //<- commenting this line will cause error
using namespace myNS;
swap(lhs.h, rhs.h);
}
int main() {
Foo f1, f2;
swap(f1, f2);
}
第 27 行发生了奇怪的事情(using std::swap;
)。如果我将其注释掉,则名称 myNS::swap 与已在全局范围中定义的签名完全相同的签名将被提升到全局范围,从而导致重载歧义错误,正如我所预期的那样。
但是,如果我不注释第 27 行并编译,则不会报告歧义错误。并且程序执行最初在全局范围内定义的 ::swap ,就像 using 指令一样using namespace myNS;
不会提升 myNS::swap,因此不会将其添加到重载的候选集中。我就是无法理解这种现象。为什么来自不相关命名空间(std 当然不包含 HasPtr 版本的 swap)的 using 声明可以协调 ADL 下的重载歧义?为什么选择执行原始的 ::swap,而不是 myNS 中的竞争对手?第 27 行对重载过程是否有任何副作用(例如,抑制提升的命名空间中的名称,以便原始名称具有更高的优先级)?谢谢您的回答。
该问题可以在 Windows 7 上的 Visual Studio 2015 Update 3 和 ubuntu 14.04 上的 GCC 4.8.4(均为 64 位)中重现。