"using namespace" 究竟是做什么的?
What does "using namespace" do exactly?
以下 C++ 测试代码没有 link(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
变量,因此 linking 错误。
问题:谁能解释一下为什么我的预期不正确,到底发生了什么?
不是答案:
- 我可以 link 将第 10 行更改为
std::string X::test = "Test";
。
- 我不应该使用 "using namespace" 开头。
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
的定义但未找到,因此您会看到此错误。
指令 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
,这确实找到了 X::C
,这要归功于 using
指令。
这里是命名空间 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指令。
以下 C++ 测试代码没有 link(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
变量,因此 linking 错误。
问题:谁能解释一下为什么我的预期不正确,到底发生了什么?
不是答案:
- 我可以 link 将第 10 行更改为
std::string X::test = "Test";
。 - 我不应该使用 "using namespace" 开头。
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
的定义但未找到,因此您会看到此错误。
指令 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
,这确实找到了 X::C
,这要归功于 using
指令。
这里是命名空间 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指令。