Возможно ли включить класс пространства имен в другое пространство имен? - PullRequest
1 голос
/ 19 апреля 2019

Я работаю через «Язык программирования C (4-е издание)» Бьярна Страуструпа, и в разделе 14.4.6 (Пространства имен - управление версиями) я наткнулся на пример, который мне не удалось воспроизвести.В примере #include объявлено пространство с объявлением класса в другом пространстве имен.Вот моя упрощенная попытка воспроизвести этот пример:

// V3.h

namespace V3 {
    class C {
        public:
            void print();
    };
}

// V3.cpp

#include <iostream>
#include "V3.h"

using namespace std;

namespace V3 {
    void C::print() {
        cout << "Hello from C" << endl;
    }
}

// Popular.h

namespace Popular {
    #include "V3.h"
}

// main.cpp

#include "Popular.h"

int main() {
    Popular::V3::C c;
    c.print();
}

Когда я пытаюсь скомпилировать эту программу, я получаю следующий вывод:

$ g++ main.cpp V3.cpp
/tmp/ccAVnUZi.o: In function `main':
main.cpp:(.text+0x1f): undefined reference to `Popular::V3::C::print()'
collect2: error: ld returned 1 exit status

Итак, мне интересноМожно ли #include класс пространства имен в другое пространство имен?Или я не смог воспроизвести этот пример по другим причинам?В следующем разделе (15.2.5) я прочитал, что это может быть невозможно.

1 Ответ

1 голос
/ 19 апреля 2019

Да, это вполне возможно (хотя и весьма сомнительно).#include на самом деле не более чем текстовое копирование-вставка.Это как если бы ваш Popular.h был:

namespace Popular {
    namespace V3 {
        class C {
            public:
                void print();
        };
    }
}

Это может быть допустимым кодом C ++, конечно, есть много случаев, когда его не будет.

Обратите внимание, что этот классC - это ::Popular::V3::C.Это другой, не связанный тип, тип которого объявлен в V3.h - ::V3::C.Второй тип имеет определение своей функции print() в V3.cpp.

Но это не тот print(), который вы вызываете - вы вызываете ::Popular::V3::C::print() (который, опять же, функция-член другого типа, чем ::V3::C), и нет определения для этой функциив любом месте.Таким образом, в результате вы получаете неопределенную ссылку - вам нужно добавить определение для этой вещи.Как, скажем,

// Popular.cpp
#include <iostream>
void Popular::V3::C::print() {
    std::cout << "This is bad and I should feel bad about it. :-(" << std::endl;
}

Но на самом деле, не #include вещи внутри namespace, если у вас нет действительно веской причины для этого.Вместо этого вы могли бы предоставить псевдоним пространства имен:

#include "V3.h"
namespace Popular {
    namespace V3 = ::V3;
}

Это позволило бы вам по-прежнему писать Popular::V3::C, который теперь фактически соответствует типу ::V3::C.

Или псевдоним типа:

#include "V3.h"
namespace Popular {
    using C = ::V3::C;
}

И здесь ::Popular::C на самом деле тот же тип, что и ::V3::C.

...