Подтвердить что ты не робот

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

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

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

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

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

EDIT: @KennyTM: в любом объединении, если элемент инициализирован, другие будут иметь значения мусора, если ни один из них не инициализирован, все будут иметь значения мусора. Я думаю, что тегированный союз просто обеспечивает некоторый комфорт для доступа к действительным значениям из Союза. Ваш вопрос: как вы или компилятор можете написать конструктор копирования для объединения выше без дополнительной информации?    sizeof (string) дает 4 байта. Исходя из этого, компилятор может сравнивать размеры других членов и распределять наибольшее распределение (4 байта в нашем примере). Внутренняя длина строки не имеет значения, поскольку она будет храниться в отдельном месте. Пусть строка имеет любую длину. Все, что должен знать Союз, - это вызов конструктора строк класса string со строковым параметром. В любом случае, как компилятор обнаруживает, что конструктор копирования должен быть вызван в обычном случае, аналогичный метод, который следует соблюдать, даже если строка находится внутри Союза. Поэтому я думаю, что компилятор мог бы сделать, выделите 4 байта. Затем, если для s назначена какая-либо строка, тогда класс string позаботится о распределении и копировании этой строки, используя собственный распределитель. Таким образом, нет никаких шансов на повреждение памяти.

Строка не существует во время разработки Союза в компиляторе? Поэтому ответ мне еще не ясен. Я новый член на этом сайте, если что-то не так, простите меня.

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

U u;  // <--

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

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

В С++ 0x это правило было смягчено (раздел 9.5/2):

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

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

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 or boost::any.

Ответ 3

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

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

Обратите внимание, что он изменяется в С++ 0x: Неограниченные объединения

union {
    int z;
    double w;
    string s;  // Illegal in C++98, legal in C++0x.
};

Ответ 4

Из спецификации С++ §9.5.1:

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

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

Ответ 5

Мусор вводится, если вы

  • назначить строку
  • затем назначьте int или float
  • затем снова строку

строка управляет памятью в другом месте. Эта информация, скорее всего, указана. Этот указатель мусор при назначении int. Назначение новой строки должно уничтожить старую строку, что невозможно.

Второй шаг должен уничтожить строку, но не знает, была ли строка.

Они, очевидно, нашли решение этой проблемы тем временем.