以下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
变量,因此链接错误。
问题:有人可以解释为什么我的期望不正确,以及发生了什么?
不是答案:
std::string X::test = "Test";
。答案 0 :(得分:27)
指令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
。
答案 1 :(得分:7)
using namespace
表示您从指定的命名空间使用定义,但并不意味着您定义的所有内容都是在您使用的命名空间中定义的。
这种行为的逻辑非常简单。我们假设我们有以下示例:
namespace X
{
extern string test;
};
namespace Y
{
extern string test;
};
using namespace X;
using namespace Y;
string test = "value";
按照您的示例逻辑,编译器根本不知道应该在哪个命名空间中定义test
,因此您必须声明命名空间显式。在现实生活中,它在全局命名空间中定义。
在您的特定情况下,您在test
命名空间之外定义X
变量,并将其声明为extern
。链接器查找X::test
的定义,但找不到,因此您会看到此错误。
答案 2 :(得分:3)
以下是名称空间test
中变量X
的声明。
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;
^^^^^
然后会出现歧义,因为由于using指令,此名称可以引用名称X::test
和::test
。