На конструктор по умолчанию нельзя ссылаться при использовании std :: string в элементе union структуры - PullRequest
0 голосов
/ 05 февраля 2019

У меня есть очень базовая структура, в которой есть перечисление и объединение.

typedef struct
{
    enum v{a,b,c}v;
    union w{
        int a;
        bool b;
        std::string c;
    }w;

}Data_Set2;

int main()
{
Data_Set2 val; // Shows errror that the default constructor cannot be referenced
return 0;
}

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

typedef struct
{
    enum v{a,b,c}v;
    union w{
        int a;
        bool b;
        std::string c;
    }; // changed here.

}Data_Set2;

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

Ответы [ 3 ]

0 голосов
/ 05 февраля 2019

Проблема в том, что объединение w не является ни конструируемым по умолчанию, ни разрушаемым.Конструктор по умолчанию и деструктор не генерируются неявно, поскольку член c не является ни тривиально, ни тривиально разрушаемым.Таким образом, наличие члена типа w просто невозможно.Во втором примере вы удаляете элемент, поэтому проблем нет.

Чтобы сделать w конструируемым по умолчанию, вы можете определить конструктор по умолчанию:

union w{
    int a;
    bool b;
    std::string c;

    w() // Could activate one of the members if so desired
    {}

чтобы сделать w разрушаемым, вы можете определить деструктор (но читать до конца):

    ~w(){
        //TODO destruct the active member
    }
} w;

Подсказки для уничтожения активного члена:

  • Невозможновыяснить, какой участник активен.
  • Доступ к неактивному члену имеет неопределенное поведение
  • Если c активен, не уничтожая его, имеет неопределенное поведение

В заключение: вынеобходимо убедиться, что w никогда не уничтожается при активном участнике c.Такой инвариант мог бы быть реализован в деструкторе Data_Set2, предполагая, что v указывает, какой элемент является активным (который является другим инвариантом, который должен поддерживаться; эти члены, вероятно, не должны быть публичными).

0 голосов
/ 05 февраля 2019

С https://en.cppreference.com/w/cpp/language/union:

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

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

Не более одного вариантного члена может иметь инициализатор элемента по умолчанию.

В вашем случае это означает, что вы должны явно объявить конструктор и десктруктор.Измените свой код на:

typedef struct
{
    enum v{a,b,c}v;
    union w{
        int a;
        bool b;
        std::string c;
        w() {}       // Explicit constructor definition
        ~w() { };    // Explicit destructor definition
}w;

}Data_Set2;

Это должно сработать.

Как уже говорилось в моем комментарии, вы должны взглянуть на std::any и std::variant.Последний обеспечивает типобезопасные союзы и, вероятно, будет лучшим выбором в вашем случае.Обратите внимание, что ваш компилятор (по-видимому, MSVC) должен поддерживать C ++ 17.

РЕДАКТИРОВАТЬ: Как прокомментировал eerorika, вам нужно будет убедиться, что вы вызываете его только на текущем активном члене.Ссылка, указанная в начале, показывает пример объединения строк и векторов и того, как она вводит много ловушек для неопределенного поведения.Поэтому, если вы просто не пытаетесь понять, что происходит за кулисами или используете POD, я бы посоветовал вам работать с std::variant.

0 голосов
/ 05 февраля 2019

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

Что касается ошибки, вам нужно создатьконструктор по умолчанию в объединении, чтобы иметь возможность правильно его инициализировать:

union w{
    int a;
    bool b;
    std::string c;

    // Default constructor initialize the string member
    w() : c()
    {}
}w;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...