Член союза имеет нетривиальный конструктор копирования
У меня есть союз, который выглядит так:
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
в объединении?
Ответы
Ответ 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