Если у объединения есть члены только встроенного типа, компилятор сам синтезирует почленные версии стандартного конструктора и функций-членов управления копированием. Но для объединений, у которых есть член типа класса, определяющего собственный стандартный конструктор или функция-член управления копированием, это не так. Если тип члена объединения определяет одну из этих функций-членов, компилятор синтезирует соответствующий член объединения как удаленный (см. раздел 13.1.6).
Например, класс >string
определяет все пять функций-членов управления копированием, а также стандартный конструктор. Если объединение будет содержать строку и не определит ее собственный стандартный конструктор или одну из функций-членов управления копированием, то компилятор синтезирует эту недостающую функцию как удаленную. Если у класса будет член типа объединения, у которого есть удаленная функция-член управления копированием, то соответствующая функция (функции) управления копированием самого класса также будет удалена.
Из-за сложностей создания и удаления членов типа класса такие объединения обычно встраивают в другой классе. Таким образом, класс получает возможность управлять состоянием при передаче из и в элемент типа класса. В качестве примера добавим в объединение член класса >string
. Определим объединение как анонимное и сделаем его членом класса >Token
. Класс >Token
будет управлять членами объединения.
Для отслеживания вида значения хранимого объединением обычно определяют отдельный объект, дискриминант (discriminant). Дискриминант позволяет различать значения, которые может содержать объединение. Для синхронизации объединения и его дискриминанта сделаем дискриминант также членом класса >Token
. Для отслеживания состояния члена объединения класс определит член типа перечисления (см. раздел 19.3).
Единственными определяемыми классом функциями будут стандартный конструктор, функции-члены управления копированием и ряд операторов присвоения, способных присваивать значение одного из типов объединения члену другого:
>class Token {
>public:
> // функции управления копированием необходимы потому, что у класса
> // есть объединение с членом типа string
> // определение конструктора перемещения и оператора присваивания при
> // перемещении остается в качестве самостоятельного упражнения
> Token(): tok(INT), ival{0} { }
> Token(const Token &t): tok(t.tok) { copyUnion(t); }
> Token &operator=(const Token&);
> // если объединение содержит строку, ее придется удалять;
> // см. раздел 19.1.2
> ~Token() { if (tok == STR) sval.~string(); }
> // операторы присвоения для установки разных членов объединения
> Token &operator=(const std::string&);
> Token &operator=(char);
> Token &operator=(int);
> Token &operator=(double);
>private:
> enum {INT, CHAR, DBL, STR} tok; // дискриминант
> union { // анонимное объединение
> char cval;
> int ival;
> double dval;
> std::string sval;
> }; // у каждого объекта класса Token есть безымянный член типа этого
> // безымянного объединения
> // проверить дискриминант и скопировать член объединения, как надо