gez*_*eza 5 c++ namespaces language-lawyer
引用标准:
using 指令指定指定命名空间中的名称可以在 using 指令出现在 using 指令之后的范围内使用。在非限定名称查找 (3.4.1) 期间,名称看起来好像是在最近的封闭名称空间中声明的,该名称空间包含 using 指令和指定名称空间。
看看这段代码:
namespace A {
int fn() { return 1; }
}
namespace Inner {
int fn() { return 2; }
namespace B {
using namespace A;
int z = fn();
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,在我知道命名空间的确切规则之前,我曾期望z将其初始化为 1,正如我所写的那样using namespace A,因此预计A::fn()将使用它。但事实并非如此,z将被初始化为2,Inner::fn()因为我引用的规则而被称为。
这种行为背后的基本原理是什么:“好像它们是在最近的包含 using 指令和指定命名空间的封闭命名空间中声明的”?
如果using namespace为该命名空间中的所有内容应用 using 声明,会有什么缺点?
注意:这是促使我提出这个问题的相关问题。
命名空间系统的一个理想特性是我称之为增量 API 兼容性的特性。也就是说,如果我向命名空间添加一个符号,那么任何以前工作的程序都应该继续工作并表示相同的意思。
现在,带有重载的普通 C++ 不是渐进式 API 兼容的:
int foo(long x) { return 1; }
int main()
{
foo(0);
}
Run Code Online (Sandbox Code Playgroud)
现在我添加了重载int foo(int x) { return 2; },程序默默地改变了意义。
无论如何,当 C++ 人员设计namespace他们想要的系统时,在增加外部 API 时,以前的工作代码不应更改选择符号的名称空间。从您的示例中,以前的工作代码类似于:
namespace A {
//no fn here, yet
}
namespace Inner {
int fn() { return 2; }
namespace B {
using namespace A;
int z = fn();
}
}
Run Code Online (Sandbox Code Playgroud)
并且z很容易初始化为2. 现在A用一个名为的符号扩充命名空间fn不会改变该工作代码的含义。
相反的情况并不真正适用:
namespace A {
int fn() { return 1; }
}
namespace Inner {
// no fn here
namespace B {
using namespace A;
int z = fn();
}
}
Run Code Online (Sandbox Code Playgroud)
这里z初始化为1. 当然,如果我添加fn到Inner它会改变程序的意义,但Inner不是外部API:实际上,当Inner最初写的,A::fn也已经存在(!它被称为),所以没有借口的人意识不到冲突。
想象一下这个 C++98 程序:
#include <iostream>
namespace A {
int move = 0;
void foo()
{
using namespace std;
cout << move << endl;
return 0;
}
}
int main()
{
A::foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我用 C++11 编译它,由于这个using规则,一切正常。如果using namespace std为该命名空间中的所有内容应用 using 声明,则该程序将尝试打印 functionstd::move而不是 A::move.