Можно ли определить классы с одинаковыми именами в одном и том же пространстве имен, но в разных вложенных проектах? - PullRequest
0 голосов
/ 08 октября 2019

У меня есть один большой проект в Eclipse, который содержит несколько вложенных проектов. Каждый из этих вложенных проектов находится в отдельной папке и компилируется и связывается отдельно. В конце каждый из них выводит отдельную статическую библиотеку. Тогда есть несколько исполняемых файлов, которые связаны с этими статическими библиотеками. В двух из этих проектов у меня есть два класса с одинаковыми именами и одинаковыми аргументами конструктора, но они имеют разные реализации, а также разные дополнительные члены. Оба класса находятся в одном пространстве имен. После компиляции каждого проекта я использую статические библиотеки, которые они создали для отдельных исполняемых файлов. Каждый исполняемый файл связан с правильным классом, они не смешивают реализации. Кажется, все работает нормально.

Проблема в том, что когда я компилирую один из классов, компилятор выдает ошибку, что я не инициализировал некоторые переменные-члены, которые на самом деле принадлежат другому классу. Во время компиляции эти классы не имеют доступа друг к другу - они не включают другой или не включают заголовки, которые затем включают другой. Они находятся в отдельных проектах, в отдельных папках и составляются отдельно. Как тогда возможно, что при компиляции первого класса компилятор каким-то образом ищет определение второго класса и выдает мне ошибку, что я не инициализировал член из него? Поскольку я использую Eclipse, и структура моих проектов выглядит следующим образом: Основной проект C ++ (который не скомпилирован) содержит в себе другие проекты C ++ как вложенные проекты (которые скомпилированы отдельно) - возможно, это связано с Eclipse? ?

Я нарушаю Правило Единого Определения, так как мои классы находятся в отдельных проектах, и они скомпилированы в отдельных компиляциях, только с некоторыми общими заголовочными файлами и никакой другой связью между ними? И если так, то как это возможно, что компилятор улавливает такую ​​проблему, но все же может получить правильное определение класса? Потому что, конечно, он все делает правильно, и все работает нормально.

Так что моя проблема - предупреждение, которое дает мне компилятор, потому что я должен очистить все предупреждения компиляции перед отправкой кода. Сам код работает нормально.

С наилучшими пожеланиями

=== Обновление после комментариев ===

Прежде всего, извинения за непонятность. Вот изображение всей структуры проекта (очевидно, я изменил названия :)):

Структура моего проекта

У меня есть проект MainProject, которыйдействует как контейнер для других проектов. Я не строю это. В nested1 у меня есть проекты, которые создают статические библиотеки, которые я затем связываю с Executable1 и Executable2. В nested2 у меня есть другие проекты, которые снова создают статические библиотеки, которые я затем связываю с Executable3.

У меня есть класс MyFooBar как nested1, так и nested2. Вот код для них обоих

// nested1/StaticLib5/MyFooBar.hpp
namespace foo
{
    namespace bar
    {
        class MyFooBar : public FooBar
        {
            public:
                   MyFooBar(int a, int b, double c);
                   // Getters and Setters
                   // Other user-defined functions
            private:
                   int memA;
                   int memB;
                   double memC;
                   int memDiff1;
        };
    }
}
// nested2/StaticLib9/MyFooBar.hpp
namespace foo
{
    namespace bar
    {
        class MyFooBar : public FooBar
        {
            public:
                   MyFooBar(int a, int b, double c);
                   // Getters and Setters
                   // Other user-defined functions
            private:
                   int memA;
                   int memB;
                   double memC;
                   int memDiff2;
                   char memDiff3;

        };
    }
}
// nested1/StaticLib5/MyFooBar.cpp
namespace foo
{
    namespace bar
    {
        MyFooBar::MyFooBar(int a, int b, double c) :
        memA{a}, memB{b}, memC{c}, memDiff1{0}
        {
        }         
    }
}
// nested2/StaticLib9/MyFooBar.cpp
namespace foo
{
    namespace bar
    {
        MyFooBar::MyFooBar(int a, int b, double c) :
        memA{a}, memB{b}, memC{c}, memDiff2{0}, memDiff3{0}
        {
        }         
    }
}

Executable1 и Executable2 не используют ни одну из библиотек в nested2 . Executable3 использует только статическую библиотеку StaticLib1 nested1 .

Не исполняемые файлы связаны как со StaticLib5, так и со StaticLib9. Кроме того, я не получаю сообщение об ошибке во время компоновки исполняемых файлов, я получаю предупреждение при компиляции любого из классов MyFooBar. Предупреждение гласит:

Member memDiff2 was not initialized in this constructor

Единственное, что может быть общим для обоих классов, - это один заголовок, где у меня есть объявление пересылки классов и некоторые определения типов, которые я не использую в самом классе.

class MyFooBar;
typedef ListWidget* MyFooBarPtr;
typedef std::vector< MyFooBarPtr > MyFooBarVec;
typedef MyFooBarVec* MyFooBarVecPtr;

Ответы [ 3 ]

0 голосов
/ 08 октября 2019

Давайте рассмотрим, что делает компилятор за кулисами: у нас есть два модуля или единицы перевода: модуль A и B.

Во время компиляции у нас будет следующее:

Module A: namespace::class_name == foo::bar 
Module B: namespace::class_name == foo::bar

Во времякомпиляция исходного кода в объектные файлы, которые будут скомпилированы отдельно, как вы заявили. Проблема здесь не в компиляторе.

Проблема возникает, когда он берет весь объектный код или единицы перевода и пытается связать все вместе, чтобы создать один исполняемый файл.

Это когда у вас возникают конфликты имен или разрешения символов, которые нарушают правило одного определения. Именно в компоновщике возникают проблемы.

Однако некоторые компиляторы, которые встроены в интегрированную среду разработки, такую ​​как Visual Studio, могут иметь некоторые флаги компилятора, которые, так сказать, «смотрят в будущее», чтобы увидеть возможные конфликты. может произойти до того, как компиляция будет выполнена, и это может генерировать предупреждения времени компиляции или даже ошибки, прежде чем вы даже сделаете это компоновщику.

Я бы предложил либо изменить имя класса или имя пространства имен, однако это было бы более удобносохранить имя пространства имен над именем класса, так как это пространство имен представляет вашу кодовую базу.

Если по какой-то причине вам нужно сохранить класс под тем же именем, то они могут быть еще одной альтернативой ито есть включить среднее внутреннее имя вложенного пространства имен, чтобы разрешить этот конфликт имен, чтобы уменьшить неоднозначность.

Например:

Module A: namespace::namespace::class_name = foo::ver_1::bar
Module B: namespace::namespace::class_name = foo::ver_2::bar

Тогда внутреннее вложенное пространство имен будет предотвращать этот конфликт имен до тех пор, покакак ты не усинg директивы, такие как:

#using namespace foo::ver_1;
#using namespace foo::ver_2;

В пределах одной видимой области видимости!

0 голосов
/ 29 октября 2019

Прежде всего, спасибо за вашу помощь. Проблема, похоже, связана с самой средой QNX Momentics. Если я создаю проекты из командной строки, предупреждения не выводятся. Если я создаю первый MyFooBar из nested1 , то один проект из nested2 , затем второй MyFooBar из nested2 , я получаю это предупреждение. Если я создаю первый MyFooBar, затем несколько других проектов из nested1 , а затем второй MyFooBar, таких предупреждений нет. В любом случае, сборка в порядке, и все работает как положено.

С наилучшими пожеланиями, Ахмед

0 голосов
/ 08 октября 2019

В C ++ существует одно правило определения, согласно которому классы / функции не могут быть определены дважды. Не говоря уже о случае, когда определения различны.

При этом существуют также правила изоляции, позволяющие обойти одно правило определения. Скажем, у вас есть два класса с одинаковыми именами, но они определены и используются только в двух разных файлах .cpp, тогда все должно работать ...

Обновление: возможно, оно скомпилируется, но оно будет работатьтак что линкер плохо справляется с этим. Это не будет работать должным образом, если определения не совпадают. Но это работает, когда у вас есть разделение на уровне проекта, то есть в разных файлах разделяемых библиотек, или .dll - требуется динамическое разделение на уровне библиотеки, .cpp или уровень разделения статических библиотек недостаточен. Спасибо @fdan и @ FrancisCugler.

В основном, если есть какие-либо проекты, которые так или иначе используют оба определения - чтобы не было сильной границы компиляции - это нарушение одного правила определения и не может быть правильно скомпилировано.

PS почему вообще есть два класса с одинаковыми именами? Это отличный источник ошибок. Если вы хотите иметь переносимый код между различными архитектурами или системами ОС, вы можете использовать #ifdef, чтобы удалить неподходящую версию класса из сборки.

...