为什么 C++ 的“使用命名空间”的工作方式如此?

Quu*_*one 26 c++ using-directives rationale name-lookup c++98

所有学生都对 C++ using-directives的行为感到惊讶。考虑这个片段(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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你可能认为test会叫NAfoo(Zoo::Lion); 但实际上它最终会调用N1's foo(Zoo::Cat)

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

图表

然后正常的非限定查找“查找”树,顺序为testN2NCNB::,一旦fooNC. 树上的 foos in NBand::没有通过查找找到,因为它们已经被fooin隐藏了NC

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

为 C++ 选择这种特殊机制的考虑因素是什么?

我正在专门寻找 C++ 设计的参考资料——书籍、博客文章、WG21 论文、D&EARM、反射器讨论,以及任何此类性质的东西。我特别不是在寻找“基于意见”的答案;我正在寻找设计该功能时指定的真正理由。

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

小智 2

引用《C++ Primer(第五版)》的摘录部分“18.2.2. 使用命名空间成员”的

\n
\n

using 指令引入的名称范围比 using 声明中的名称范围更复杂\n。正如我们\xe2\x80\x99 所见,using 声明将名称置于与 using 声明本身相同的作用域中。这就好像 using 声明为命名空间成员声明了一个本地别名。

\n

using 指令不声明本地别名。相反,它的作用是将命名空间成员提升到最近的范围,其中包含命名空间本身和 using 指令。

\n

using 声明和 using 指令之间的范围差异直接源于这两个工具的工作方式。在使用 using 声明的情况下,我们只是让 name 在本地范围内可直接访问。相比之下,using 指令使命名空间的全部内容可用。通常,命名空间可能包含不能出现在本地作用域中的定义。因此,using 指令被视为出现在最近的封闭命名空间范围中。

\n
\n

因此,与using 声明相比, using 指令的作用域处理差异不会阻止声明相同的名称。当发生此类名称冲突时,将允许使用,但任何不明确的用法都必须明确指定预期版本。

\n