Структуры с тем же именем / пространством имен, но разными членами - PullRequest
0 голосов
/ 06 ноября 2018

Я вижу странное поведение, которое я описал в этом посте вектор встроенной инициализации структур. приготовился-INIT-лист

Я надеюсь устранить один из возможных источников неопределенного поведения. У меня есть следующие два класса A и B, которые оба вперед объявляют struct Foo, но файл cpp определяет фактическую структуру, как показано ниже.

// This is A.h
namespace app {
    struct Foo;
    class A {
    private:
        std::vector<Foo> fooList;
    };
}

// This is A.cpp
struct app::Foo {
    std::string first;
    std::string second;
    unsigned flag;
};


// This is B.h
namespace app {
    struct Foo;
    class B {
    private:
        std::vector<Foo> fooList;
    };
}

// This is B.cpp
struct app::Foo {
    std::string first;
    std::string second;
    bool flag;
    float value;
};

Обратите внимание, что структура Foo имеет разные члены в файлах cpp A.cpp, B.cpp. Классы A и B никогда не выставляют члена fooList. Возможно ли когда-либо, что предварительное объявление точно такое же, в файлах A.h и B.h для struct Foo сгенерированный код может использовать один или другой. Это могло бы объяснить проблему, которую я видел в связанной проблеме.

Другими словами, при использовании braced-init-list для структуры Foo, вызванной в B.cpp, гарантируется, что будет использоваться Foo, определенный в B.cpp, или одинаково вероятно, что Foo определенный в A.cpp также может быть использован?

Даже когда я пишу это, я сразу понимаю, что эта реализация плохая практика, так как Foo сама является внутренней по отношению к классам A и B и должна действительно быть объявлена ​​в закрытой секции класса сам по себе.

1 Ответ

0 голосов
/ 06 ноября 2018

Это нарушает ODR (одно правило определения).

Программа некорректна, диагностика не требуется.

Абсолютно любое поведение допускается стандартом C ++, если вы делаете это. Он может «работать», он может выбрать один и отбросить другой, он может работать, пока вы не перезапустите, он может отформатировать ваш жесткий диск.

Я сделал это в реальном живом проекте; у нас был матричный заголовок, в котором вы могли бы определить токены, прежде чем включать его, если он будет поддерживать float или double.

Это "сработало", хотя мы никогда не использовали обе версии в одной и той же DLL. Тогда мы использовали обе версии.

Компилятор выбирает один или другой размер для структуры на основе одного набора совпадений и выбирает один или другой конструктор на основе немного другого набора совпадений. Мы получили извращение памяти в изобилии. Но только иногда на некоторых сборках.

Мы быстро «исправили» его, поместив код в пространство имен, имя которого включало скалярный тип, а затем using перенесли их во внешнее пространство имен.

...