Почему компилятор не разрешает std :: string внутри union?

Я хочу использовать строку внутри Union. , если я напишу, как показано ниже

union U
{
   int i;
   float f;
   string s;
};

Компилятор выдает ошибку, говоря, что U :: S имеет конструктор копирования.

Я прочитал другой пост об альтернативных способах решения этой проблемы. Но я хочу знать, почему компилятор вообще не позволяет этого?

РЕДАКТИРОВАТЬ: @KennyTM: В любом союзе, если член инициализирован, другие будут иметь значения мусора, если ни один не инициализирован, все будут иметь значения мусора. Я думаю, что tagged union просто обеспечивает некоторый комфорт для доступа к действительным значениям от Union. Ваш вопрос: как вы или компилятор пишете конструктор копирования для указанного выше объединения без дополнительной информации? sizeof (string) дает 4 байта. На основе этого компилятор может сравнить размеры других членов и выделить наибольшее выделение (4 байта в нашем примере). Длина внутренней строки не имеет значения, потому что она будет храниться в отдельном месте.Пусть строка будет любой длины. Все, что нужно знать Union, - это вызвать конструктор копирования строкового класса со строковым параметром. Каким бы способом компилятор ни обнаружил, что конструктор копирования должен быть вызван в обычном случае, аналогичный метод, которому следует следовать, даже если строка находится внутри Union. Итак, я думаю, что компилятор мог бы выделить 4 байта. Затем, если какая-либо строка присваивается s, тогда строковый класс позаботится о выделении и копировании этой строки, используя свой собственный распределитель. Так что шансов на повреждение памяти тоже нет.

Разве строка не существовала во время разработки Union в компиляторе? Так что ответ мне до сих пор не ясен. Я новичок на этом сайте, если что-то не так, пожалуйста, извините меня.

45
задан bjskishore123 20 August 2010 в 07:33
поделиться

4 ответа

Подумайте об этом. Как компилятор узнает, какой тип находится в объединении?

Не знает. Основная операция объединения - это поразрядное приведение. Операции со значениями, содержащимися в объединениях, безопасны только тогда, когда каждый тип может быть заполнен мусором. std :: string не может, потому что это приведет к повреждению памяти. Используйте boost :: option или boost :: any .

26
ответ дан 26 November 2019 в 21:04
поделиться

Из спецификации C++ §9. 5.1:

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

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

7
ответ дан 26 November 2019 в 21:04
поделиться

Потому что наличие класса с нетривиальным конструктором (copy /) в объединении не имеет смысла. Предположим, у нас есть

union U {
  string x;
  vector<int> y;
};

U u;  // <--

. Если бы U была структурой, u.x и u.y были бы инициализированы пустой строкой и пустым вектором соответственно. Но у членов профсоюза один и тот же адрес. Таким образом, если инициализируется u.x , u.y будет содержать недопустимые данные, и наоборот. Если они оба не инициализированы, их нельзя использовать. В любом случае, объединение этих данных не может быть легко обработано, поэтому C ++ 98 предпочитает отрицать это: (§9.5 / 1):

Объект класса с нетривиальным конструктором (12.1), Нетривиальный конструктор копирования (12.8), нетривиальный деструктор (12.4) или нетривиальный оператор присваивания копии (13.5.3, 12.8) не могут быть членом объединения, как и массив таких объектов.

В C ++ 0x это правило было ослаблено (§9.5 / 2):

Максимум один нестатический член данных объединения может иметь фигурную скобку или равный инициализатор . [ Примечание: , если какой-либо нестатический член данных объединения имеет нетривиальный конструктор по умолчанию (12.1), конструктор копирования (12.8), конструктор перемещения (12.8), оператор присваивания копии (12.8), перемещение оператор присваивания (12.8) или деструктор (12.4), соответствующая функция-член объединения должна быть предоставлена ​​пользователем, иначе она будет неявно удалена (8.4.3) для объединения. - конечное примечание ]

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

struct TU {
   int type;
   union {
     int i;
     float f;
     std::string s;
   } u;

   TU(const TU& tu) : type(tu.type) {
     switch (tu.type) {
       case TU_STRING: new(&u.s)(tu.u.s); break;
       case TU_INT:    u.i = tu.u.i;      break;
       case TU_FLOAT:  u.f = tu.u.f;      break;
     }
   }
   ~TU() {
     if (tu.type == TU_STRING)
       u.s.~string();
   }
   ...
};

Но, как упомянул @DeadMG , это уже реализовано как boost :: variant или boost :: any .

58
ответ дан 26 November 2019 в 21:04
поделиться

В C ++ 98/03 члены объединения не могут иметь конструкторов, деструкторов, виртуальных функций-членов или базовых классов.

Таким образом, вы можете использовать только встроенные типы данных или POD

Обратите внимание, что в C ++ 0x это меняется: Unrestricted unions

union {
    int z;
    double w;
    string s;  // Illegal in C++98, legal in C++0x.
};
14
ответ дан 26 November 2019 в 21:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: