Подтвердить что ты не робот

Что делает "использование пространства имен" точно?

Следующий тестовый код С++ не связан (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 определит переменную X::test, объявленную в строке 06. Я считаю, что вместо нее объявлена ​​и определена не связанная с ней переменная test в глобальном пространстве имен, следовательно, ошибка связывания.

Вопрос: Может кто-нибудь объяснить, почему мое ожидание было неправильным, и что происходит точно?

Не ответ:

  • Я могу сделать ссылку на изменение строки 10 на std::string X::test = "Test";.
  • Мне не следует использовать "using namespace" для начала.
4b9b3361

Ответ 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, которое действительно находит X::C благодаря директиве using.

Ответ 2

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. Linker ищет определение X::test, но не находит и в результате вы видите эту ошибку.

Ответ 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.

Что касается директивы use, то он вводит имена, объявленные в указанном пространстве имен в пространстве имен, где используется директива.

Например, если писать с использованием неквалифицированного имени test

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

тогда будет двусмысленность, потому что это имя может ссылаться на имя X::test и ::test из-за директивы use.