26

所有学生都对 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());
            }
        }
    }
}

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

原因是它using namespace NA实际上并没有将名称从当前范围中NA引入;它将它们带入 and 的最不常见的祖先范围,即. 并且不会将名称带入当前范围;它将它们带入 and 的最不常见的祖先范围,即. 在这里,我画了一个漂亮的图表:NAN2::using namespace N1N1N1N2NC

图表

然后正常的非限定查找“查找”树,顺序为test–<code>N2–<code>NC–<code>NB–<code>::,一旦fooNC. 树上较高的 foo , inNB::,没有通过查找找到,因为它们已被fooin隐藏NC

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

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

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

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

4

1 回答 1

2

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

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

using 指令不声明本地别名。相反,它具有将命名空间成员提升到包含命名空间本身和 using 指令的最近范围的效果。

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

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

于 2020-12-28T13:38:58.047 回答