【问题标题】:What does "using namespace" do exactly?“使用命名空间”到底是做什么的?
【发布时间】:2016-03-11 19:03:47
【问题描述】:

以下 C++ 测试代码未链接(gcc 4.9.2、binutils 2.25)。错误是In function 'main': undefined reference to 'X::test'

01: #include <string>
02: #include <iostream>
03:
04: namespace X
05: {
06:     extern std::string test;
07: };
08:
09: using namespace X;
10: std::string test = "Test";
11:
12: int main()
13: {
14:    std::cout << X::test << std::endl;
15: }

由于第 09 行,我希望第 10 行定义在第 06 行声明的 X::test 变量。我相信相反,在全局命名空间中声明和定义了一个不相关的 test 变量,因此出现链接错误。

问题:谁能解释一下为什么我的预期不正确,究竟发生了什么?

不是答案:

  • 我可以将第 10 行更改为std::string X::test = "Test";
  • 我不应该使用“使用命名空间”开头。

【问题讨论】:

  • 既然你说在命名空间extern 内,它会在命名空间外寻找它。它没有在命名空间内定义,编译器也找不到它,Undefined reference。向您提出的问题:为什么它需要是外部的?
  • @wouter140: extern 与“在命名空间之外寻找东西”无关。它只是意味着“这是在其他地方(外部)定义的”。
  • @curiousguy 字面意思是“其他地方”,或者实际上是其他任何地方。它只是意味着“这只是一个声明,而不是一个定义。”
  • 也许你可以发布没有行号的代码,或者在 cmets 中使用数字。不是每个人都使用允许矩形选择的文本编辑器(就像 vim 和 emacs 一样)。
  • @curiousguy 和 Notepad++ 一样(也可以命名一个非主要的 Unix 世界编辑器)。

标签: c++ scope namespaces forward-declaration using-directives


【解决方案1】:

指令using namespace X; 使命名空间X 中的名称在包含该指令的命名空间内可见。也就是说,在该范围内查找名称n 时,可以找到X::n。但是,只有在编译器需要查找它时才会查找它。

在您的示例中,此声明:

std::string test = "Test";

在全局命名空间内是完全合理的。名称test 是简单介绍的,与任何其他声明一样。无需在任何地方查找。

这将是一锅完全不同的鱼:

namespace X
{
  struct C
  {
    static std::string test;
  };
}

using namespace X;
std::string C::test = "Test";

在这段代码中,编译器需要知道C 是什么,才能理解C::test 的定义。因此,它执行C 的名称查找,由于using 指令,它确实找到了X::C

【讨论】:

  • 找到答案。结构的场景也很有趣。谢谢。
【解决方案2】:

using namespace 表示您使用来自您指定的命名空间的定义,但这并不意味着您定义的所有内容都在您使用的命名空间中定义。

这种行为的逻辑非常简单。假设我们有以下示例:

namespace X
{
    extern string test;
};

namespace Y
{
    extern string test;
};

using namespace X;
using namespace Y;

string test = "value";

按照您的示例逻辑,编译器只是不知道它应该在哪个命名空间中定义test,因此您必须显式地声明命名空间。在现实生活中,它是在 global 命名空间中定义的。

在您的特定情况下,您在 X 命名空间之外定义 test 变量,该命名空间被声明为 extern。链接器查找 X::test 的定义,但没有找到,因此您会看到此错误。

【讨论】:

  • 我同意您的代码存在歧义。但是,我认为您没有成功解释为什么在我的示例中编译器决定在全局命名空间中声明和定义一个新变量,而不是在我正在使用的命名空间中查找现有变量。
  • @marcv81 可能,我的解释不是最好的,但在这种情况下,如果你想要一个全局命名空间中的变量,你需要将它定义为 ::test 并且它仍然是 显式 声明。一般来说,编译器会照你说的做,不应该猜你想要什么。
  • @marcv81:关键是当定义一个标识符时,编译器不会“搜索”声明,根本。 (标识符在定义之前不需要声明。)语句std::string test = ...定义一个变量test在当前命名空间中using namespace ...不影响;它只影响为标识符搜索的命名空间。标识符只有在引用时才会被“搜索”——而定义不会。这正是亚历克斯想说的。
  • @marcv81:更好的风格是相反的,总是X::从不using namespace X;。后者确实是一个拐杖,因为它会从源头上夺走信息。 (您无法分辨给定标识符来自哪个命名空间,这是一个皇家 PITA,当调试源时“使用命名空间”对其中的六个进行了“使用命名空间”。如果命名空间名称太长而无法重复输入,请使用 namespace short = very::long::namespace; 和 @ 987654335@.
  • @DevSolar:从不using namespace 是一个好规则,但总是X:: 不是。限定名称会破坏 ADL。在某些情况下,在本地范围内应用 using 声明以将命名空间中的各个名称引入重载集中要好得多,但让重载决议选择最后是否选择这些名称。
【解决方案3】:

这是命名空间X 中变量test 的声明。

04: namespace X
05: {
06:     extern std::string test;
07: };

这不是变量的定义。该变量也必须先定义,然后才能用于获取其值。

如果您初始化变量,您也可以将此声明作为定义。例如

04: namespace X
05: {
06:     extern std::string test = "Test";
07: };

在这种情况下,代码将成功编译。

在此声明中

14:    std::cout << X::test << std::endl;

可以访问限定名称X::test。编译器在命名空间X 中搜索该名称,因为它在变量中指定并找到声明。现在它需要获取变量的值但找不到它的定义。

在此声明中

10: std::string test = "Test";

在全局命名空间中有声明和定义的变量test,因为它是在任何显式指定的命名空间之外声明的。

你可以写

10: std::string X::test = "Test";
                ^^^^^^^

而不是

10: std::string test = "Test";

如果要定义命名空间X中声明的变量。

至于 using 指令,则它在使用该指令的命名空间中引入在指定命名空间中声明的名称。

例如如果使用非限定名称test编写

14:    std::cout << test << std::endl;
                    ^^^^^

那么会有歧义,因为这个名字可以引用名字X::test::test由于using指令。

【讨论】:

    猜你喜欢
    • 2018-05-27
    • 1970-01-01
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    相关资源
    最近更新 更多