- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这是问题 here 的跟进关于使用参数依赖查找 (ADL) 的函数重载。我想检查一下我在这种情况下对规则的理解,所以我写了一些测试代码。
首先,在 std 中当然没有 HasPtr 类的交换,所以我写了一个我自己的命名空间,除了已经在全局范围内定义的那个之外,它还包含一个 HasPtr 版本的交换。 using 声明按我的预期工作——产生了歧义错误,因为已经定义了交换的 HasPtr 版本,如“C++ 入门”,第 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,因此它不会被添加到候选集中进行重载。我只是无法理解这种现象。为什么来自不相关命名空间的 using 声明(std 当然不包含交换的 HasPtr 版本)可以调和 ADL 下的重载歧义?为什么选择执行的是原来的::swap,而不是它在 myNS 中的竞争对手?第 27 行是否对重载过程有任何副作用(例如,从提升的命名空间中抑制名称,以便原始名称具有更高的优先级)?感谢您的回答。
此问题可以在 Windows 7 上的 Visual Studio 2015 Update 3 和 ubuntu 14.04 上的 GCC 4.8.4 中重现,均为 64 位。
最佳答案
这里的机制有三方面。
using 声明,就像using std::swap
一样,是一个声明。它将 swap
的声明引入函数的声明区域。
另一方面,using 指令不会将声明引入当前声明区域。它只允许非限定查找处理指定命名空间中的名称,就好像它们是在当前声明区域最近的封闭命名空间中声明的。
较小声明区域中的声明隐藏较大封闭声明区域中的声明。
关于上述内容,您的设置方式如下:
std::swap
在 swap(Foo, Foo)
中声明。myNS
中的名称可用于 swap(Foo, Foo)
,就好像它们是在同一个命名空间中用它声明的一样。::swap
可以被 ADL 找到(尽管也被 #1 隐藏),但是 myNS::swap
不能。由于 myNS
版本是隐藏的,ADL 也找不到它,因此它不会与任何内容发生冲突。当您删除 std::swap
的声明时,现在您可以看到 myNS::swap
。 ADL 也会找到 ::swap
,为您提供两个重载。它们都是有效的重载,并且会产生明显的歧义。
关于c++ - 为什么(不相关的)using 声明可以通过参数依赖查找调和重载歧义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46785093/
我参加了一些类(class)并阅读了 YAGNI 的用途。但是,作为一个整体,这个原则从来没有让我满意。它引入了一个逻辑悖论。 作为一个假设,您正在设计一个打算向前扩展的框架。 YAGNI(可能还有
我是一名优秀的程序员,十分优秀!