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

Член союза имеет нетривиальный конструктор копирования

У меня есть союз, который выглядит так:

union {
  int intValue;
  double doubleValue;
  std::string stringValue;
  void *pointerValue;
} values;

Когда я его компилирую, я получаю это сообщение об ошибке (да, я сделал #include <string>):

./Value.hh:19:19: error: union member 'stringValue' has a non-trivial copy constructor                 
      std::string stringValue;                                                                         
                  ^                                                                                    
/Developer/SDKs/MacOSX10.7.sdk//usr/include/c++/4.2.1/bits/basic_string.h:434:7: note: because         
      type 'std::basic_string<char>' has a user-declared copy constructor                              
      basic_string(const basic_string& __str);                                                         
      ^

Я скомпилирую его с помощью этой команды:

$ clang++ *.cc -isysroot /Developer/SDKs/MacOSX10.7.sdk/ -shared

Как я могу использовать std::string в объединении?

4b9b3361

Ответ 1

Вы не можете.

Объединение объединяет две функциональные возможности: возможность хранения объекта, который может иметь выбранное количество типов, а также возможность эффективного (и реализуемого) преобразования между этими типами. Вы можете поместить целое число и посмотреть его представление как двойное. И так далее.

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

В конце концов, объект union действительно не имеет понятия того, какой тип данных он фактически хранит. Он не хранит данные одного типа; он сохраняет их все одновременно. Это зависит от вас, чтобы иметь возможность ловить правильный тип. Итак, как это могло бы разумно скопировать одно значение объединения в другое?

Члены объединения должны быть типа POD (plain-old-data). И хотя С++ 11 ослабляет эти правила, объекты все равно должны иметь конструктор копирования по умолчанию (или иначе тривиальный). Конструктор копирования std::string является нетривиальным.

Скорее всего, это boost::variant. Это объект, который может хранить несколько возможных типов, подобно объединению. Однако, в отличие от профсоюза, он безопасен по типу. Поэтому он знает, что на самом деле находится в союзе; поэтому он может копировать себя и в противном случае вести себя как обычный объект С++.

Ответ 2

Вы не можете поместить a std::string в объединение. Он запрещен языком С++, потому что он небезопасен. Подумайте, что в большинстве реализаций std::string есть указатель на некоторую динамическую память, которая содержит значение строки. Учтите также, что нет способа узнать, какой член профсоюза в данный момент активен.

Реализация не может вызвать деструктор std::string, поскольку он не знает, что объект std::string является текущим активным членом, но если он не вызывает деструктор, тогда будет пропущена память.

Ответ 3

В соответствии со стандартом С++ §9.5.1:

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

Следовательно, члены объединения не могут иметь конструкторы, деструкторы, виртуальные функции-члены или базовые классы. Следовательно, вы не можете использовать std::string как член объединения.

Альтернативное решение:

Вы можете использовать boost:: variant или boost:: any.

Ответ 4

К сожалению, вы не можете использовать типы non-POD (простые старые данные) в объединении. Довольно типичным и простым обходным путем для этого является объединение объединения в структуру, и переместите экземпляр не-POD изнутри объединения в структуру.

Например:

struct Value {
    union {
        int intValue;
        double doubleValue;
        void *pointerValue;
    };
    std::string stringValue;
};

Value value;

// Demonstration of accessing members:

value.intValue = 0;
value.doubleValue = 0.0;
value.pointerValue = NULL;
value.stringValue = "foo";

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

Ответ 5

union не может быть членом следующих типов. §9.5/1:

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

Таким образом, вы определяете указатель на std::string как:

union {
  int intValue;
  double doubleValue;
  std::string *stringValue; //pointer
  void *pointerValue;
} values;

или, используйте boost union, который известен как Boost.Variant