【问题标题】:Why does C++'s "using namespace" work the way it does?为什么 C++ 的“使用命名空间”会以它的方式工作?
【发布时间】:2021-03-29 14:49:41
【问题描述】:

所有学生都对 C++using-directives 的行为感到惊讶。考虑一下这个 sn-p (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 会调用NAfoo(Zoo::Lion);但实际上它最终会调用N1foo(Zoo::Cat)

原因是using namespace NA 实际上并没有将NA 中的名称带入当前 范围;它将它们带入NAN2最小共同祖先 范围内,即::。并且using namespace N1 不会将N1 中的名称带入 current 范围;它将它们带入N1N2 的最小共同祖先范围,即NC。在这里,我画了一个漂亮的图表:

然后正常的非限定查找“查找”树,顺序为testN2NCNB::,一旦在@中找到名称foo就停止987654346@。查找树上较高的 NB:: 中的 foo,因为它们已被 NC 中的 foo 隐藏。

我认为我对这种机制的理解足以解释它是如何工作的。我不明白的是为什么。在设计 C++ 命名空间时,为什么他们选择这种完全奇怪的机制,而不是像“名称被引入当前范围,就像使用 using-declaration”或“名称被带入全局范围”或“名称保留在原处,在每个“使用”命名空间中进行单独查找,然后合并在一起”?

在为 C++ 选择这种特定机制时有哪些考虑?

我正在寻找有关 C++ 设计的参考资料——书籍、博客文章、WG21 论文、D&EARM、反射器讨论、任何那种性质。我特别在寻找“基于意见”的答案;我正在寻找设计该功能时指定的真正理由。

(我已阅读 The Design and Evolution of C++ (1994),第 17.4 节“命名空间”,但它甚至没有提到这种古怪的机制。D&E说:“using-directive 不会将名称引入本地范围;它只是使命名空间中的名称可访问。”这是 true,但缺乏理由。我希望 StackOverflow 可以做得更好。)

【问题讨论】:

  • 如果 Stroustrup 不解释他的推理,那推理只能推断。我不确定你是否能得到一个完美的答案,但我非常愿意让这个问题保持开放状态,看看人们会想出什么。

标签: c++ c++98 name-lookup using-directives rationale


【解决方案1】:

引用C++ Primer (5th Edition) 部分“18.2.2. 使用命名空间成员”的摘录

using 指令引入的名称范围更复杂 比 using 声明中的名称范围。正如我们所见,使用 声明将名称置于与 using 相同的范围内 声明本身。就好像 using 声明为 命名空间成员。

using 指令不声明本地别名。反而有效果 将命名空间成员提升到最近的范围内 命名空间本身和 using 指令。

using 声明和 using 之间的范围差异 指令直接源于这两个设施的工作方式。如果是 一个 using 声明,我们只是让 name 在 局部范围。相反, using 指令使 可用的命名空间 通常,命名空间可能包含定义 不能出现在本地范围内。因此,使用指令 被视为出现在最近的封闭命名空间范围内。

因此,与 using-declarations 相比,using-directives 范围处理的差异不会阻止声明相同的名称。当发生这种名称冲突时,它是允许的,但任何模棱两可的用法都必须明确指定预期的版本。

【讨论】:

  • 我可能会要求对此进行原始研究,但是,作者所说的“命名空间可能包含不能出现在本地范围内的定义”可能是什么意思?似乎他们在说有些东西可以通过 using namespace 拉入,而不能通过 using-declaration 拉入。我能想到的唯一不能出现在本地范围内的是模板,比如godbolt.org/z/EjT8Eo——但我们可以通过 using-declarations 或 using-directives 很好地拖入模板。
  • ...我立刻意识到,嵌套的 namespace 声明可以被using namespace N1 拉入,但不能被using N1::N2 拉入! (命名空间别名有不同的语法:namespace N2 = N1::N2。)不过,我仍然不明白“作为结果”位。
猜你喜欢
  • 2013-12-12
  • 2012-01-07
  • 2016-08-05
  • 2021-11-10
  • 2014-10-03
  • 2017-12-23
  • 1970-01-01
  • 2015-11-16
相关资源
最近更新 更多