提问人:Quuxplusone 提问时间:12/19/2020 最后编辑:TylerHQuuxplusone 更新时间:4/18/2022 访问量:1133
为什么 C++ 的“使用命名空间”会以这种方式工作?
Why does C++'s "using namespace" work the way it does?
问:
所有学生都对 C++ 使用指令的行为感到惊讶。考虑以下代码片段 (Godbolt):
namespace NA {
int foo(Zoo::Lion);
}
namespace NB {
int foo(Zoo::Lion);
namespace NC {
namespace N1 {
int foo(Zoo::Cat);
}
namespace N2 {
int test() {
using namespace N1;
using namespace NA;
return foo(Zoo::Lion());
}
}
}
}
你可能会认为会叫 ;但实际上它最终调用了 .test
NA
foo(Zoo::Lion)
N1
foo(Zoo::Cat)
原因是它实际上并没有将名称带入当前范围;它使它们进入 和 的最小共同祖先范围,即 。并且不会将名称从当前范围带入;它使它们进入 和 的最小共同祖先范围,即 。在这里,我画了一个漂亮的图表:using namespace NA
NA
NA
N2
::
using namespace N1
N1
N1
N2
NC
然后,正常的不合格查找按顺序“查找”树,顺序为 ––––,一旦在 中找到名称,就会停止。树上更高的 foos in 和 不会通过查找找到,因为它们已被 in 隐藏。test
N2
NC
NB
::
foo
NC
NB
::
foo
NC
我想我对这种机制的理解足以解释它是如何工作的。我不明白的是为什么。在设计C++命名空间时,为什么他们选择这种完全奇怪的机制,而不是更直接的机制,例如“名称被引入当前范围,就像使用声明一样”或“名称被引入全局范围”或“名称留在原地,在每个'使用'命名空间中进行单独的查找,然后合并在一起”?
为 C++ 选择这种特定机制时考虑了哪些因素?
我专门寻找对C++设计的参考——书籍、博客文章、WG21论文、D&E、ARM、反射器讨论,任何类似的东西。我特别不是在寻找“基于意见”的答案;我正在寻找设计该功能时指定的真正理由。
(我读过 The Design and Evolution of C++ (1994),第 17.4 节“命名空间”,但它甚至没有提到这个古怪的机制。D&E 说:“using 指令不会将名称引入本地范围;它只是使命名空间中的名称可访问。这是真的,但缺乏理由;我希望 Stack Overflow 能做得更好。
答:
引用C++入门(第 5 版)部分“18.2.2.使用命名空间成员”
using 指令引入的名称范围更为复杂 比使用声明时的名称范围。正如我们所看到的,使用 声明将名称置于与 using 相同的作用域中 声明本身。就好像 using 声明声明了 命名空间成员。
using 指令不声明本地别名。相反,它有效果 将命名空间成员提升到包含 命名空间本身和 using 指令。
using 声明和 using 声明之间的此范围差异 指令直接源于这两个设施的运作方式。在以下情况下 一个使用声明,我们只是让名称直接访问 本地范围。相反,using 指令使 命名空间可用 通常,命名空间可能包含定义 不能出现在本地范围内。因此,using 指令 被视为它出现在最近的封闭命名空间作用域中。
因此,与 using 声明相比,using 指令在作用域处理方面的这种差异是为了不阻止声明相同的名称。当发生此类名称冲突时,将允许这样做,但任何模棱两可的用法都必须明确指定预期的版本。
评论
using namespace
using namespace N1
using N1::N2
namespace N2 = N1::N2
评论