Union declaration
Объединение — это специальный тип класса, который может содержать только один из своих нестатических членов-данных в каждый момент времени.
Содержание |
Синтаксис
Спецификатор класса для объявления объединения похож на class или struct объявление:
union
attr
class-head-name
{
member-specification
}
|
|||||||||
| attr | - | (since C++11) опциональная последовательность любого количества attributes |
| class-head-name | - | имя объединения, которое определяется. Может быть предварено nested-name-specifier (последовательностью имён и операторов разрешения области видимости, заканчивающейся оператором разрешения области видимости). Имя может быть опущено, в этом случае объединение является безымянным |
| member-specification | - | список спецификаторов доступа, объявлений и определений членов-объектов и членов-функций. |
Объединение может иметь функции-члены (включая конструкторы и деструкторы), но не может иметь виртуальные функции.
Объединение не может иметь базовые классы и не может использоваться в качестве базового класса.
|
Не более одного вариантного члена может иметь инициализатор члена по умолчанию . |
(since C++11) |
Объединение не может иметь нестатические элементы данных ссылочных типов.
|
Объединения не могут содержать нестатический элемент данных с нетривиальной специальной функцией-членом . |
(до C++11) |
|
Если объединение содержит нестатический элемент данных с нетривиальной специальной функцией-членом , соответствующая специальная функция-член объединения может быть определена как удалённая, подробности см. на соответствующей странице специальных функций-членов. |
(начиная с C++11) |
Так же, как и в объявлении struct , доступ к членам по умолчанию в объединении является public .
Объяснение
Объединение имеет размер как минимум достаточный для хранения его самого большого элемента данных, но обычно не больше. Остальные элементы данных размещаются в тех же байтах, что и часть этого самого большого элемента. Детали этого размещения определяются реализацией, за исключением того, что все нестатические элементы данных имеют один и тот же адрес. Чтение из элемента объединения, в который не производилась последняя запись, является неопределённым поведением. Многие компиляторы реализуют, в качестве нестандартного расширения языка, возможность чтения неактивных элементов объединения.
#include <cstdint> #include <iostream> union S { std::int32_t n; // занимает 4 байта std::uint16_t s[2]; // занимает 4 байта std::uint8_t c; // занимает 1 байт }; // все объединение занимает 4 байта int main() { S s = {0x12345678}; // инициализирует первый член, s.n теперь активный член // В этой точке чтение из s.s или s.c является неопределенным поведением, // но большинство компиляторов определяют его. std::cout << std::hex << "s.n = " << s.n << '\n'; s.s[0] = 0x0011; // s.s теперь активный член // В этой точке чтение из s.n или s.c является неопределенным поведением, // но большинство компиляторов определяют его. std::cout << "s.c is now " << +s.c << '\n' // 11 или 00, в зависимости от платформы << "s.n is now " << s.n << '\n'; // 12340011 или 00115678 }
Возможный вывод:
s.n = 12345678 s.c is now 0 s.n is now 115678
Каждый член размещается так, как если бы он был единственным членом класса.
|
Если членами объединения являются классы с пользовательскими конструкторами и деструкторами, для переключения активного члена обычно требуется явный вызов деструктора и размещающий new:
Запустить этот код
#include <iostream> #include <string> #include <vector> union S { std::string str; std::vector<int> vec; ~S() {} // нужно знать, какой член активен, возможно только в union-подобном классе }; // всё объединение занимает max(sizeof(string), sizeof(vector<int>)) int main() { S s = {"Hello, world"}; // в этой точке чтение из s.vec является неопределённым поведением std::cout << "s.str = " << s.str << '\n'; s.str.~basic_string(); new (&s.vec) std::vector<int>; // теперь s.vec является активным членом объединения s.vec.push_back(10); std::cout << s.vec.size() << '\n'; s.vec.~vector(); } Вывод: s.str = Hello, world 1 |
(начиная с C++11) |
Если два члена объединения являются стандартными типами размещения , корректно определено исследование их общей подпоследовательности на любом компиляторе.
Время жизни члена
Время жизни члена union начинается, когда член становится активным. Если до этого был активен другой член, его время жизни завершается.
Когда активный член объединения изменяется выражением присваивания вида
E1 = E2
, использующим либо встроенный оператор присваивания, либо тривиальный оператор присваивания, для каждого члена объединения X, присутствующего в подвыражениях доступа к члену и индексации массива в
E1
, который не является классом с нетривиальными или удаленными конструкторами по умолчанию, если модификация X будет иметь неопределенное поведение согласно правилам псевдонимов типов, объект типа X неявно создается в указанном хранилище; инициализация не выполняется, и начало его времени жизни упорядочивается после вычисления значений левого и правого операндов, но до присваивания.
union A { int x; int y[4]; }; struct B { A a; }; union C { B b; int k; }; int f() { C c; // не начинает время жизни ни одного члена union c.b.a.y[3] = 4; // OK: "c.b.a.y[3]" именует члены union c.b и c.b.a.y; // Это создает объекты для хранения членов union c.b и c.b.a.y return c.b.a.y[3]; // OK: c.b.a.y ссылается на вновь созданный объект } struct X { const int a; int b; }; union Y { X x; int k; }; void g() { Y y = {{1, 2}}; // OK, y.x является активным членом union int n = y.x.a; y.k = 4; // OK: завершает время жизни y.x, y.k становится активным членом union y.x.b = n; // неопределенное поведение: y.x.b изменен вне своего времени жизни, // "y.x.b" именует y.x, но конструктор по умолчанию X удален, // поэтому время жизни члена union y.x не начинается неявно }
Тривиальный конструктор перемещения, оператор перемещающего присваивания, (начиная с C++11) конструктор копирования и оператор копирующего присваивания для объединений копируют представления объектов. Если источник и приемник не являются одним и тем же объектом, эти специальные функции-члены начинают время жизни каждого объекта (кроме объектов, которые не являются подобъектами ни приемника, ни неявно-временных типов ), вложенного в приемник, соответствующего вложенному в источнике, до выполнения копирования. В противном случае они ничего не делают. Два объекта объединения имеют один и тот же соответствующий активный член (если есть) после конструирования или присваивания с помощью тривиальных специальных функций.
Анонимные объединения
Анонимный объединение — это безымянное определение объединения, которое одновременно не определяет никакие переменные (включая объекты типа объединения, ссылки или указатели на объединение).
union
{
спецификация-членов
}
;
|
|||||||||
Анонимные объединения имеют дополнительные ограничения: они не могут иметь функций-членов, не могут иметь статических членов данных, и все их члены данных должны быть публичными. Единственными разрешенными объявлениями являются нестатические члены данных
и
static_assert
объявления
(начиная с C++11)
.
Члены анонимного объединения внедряются в охватывающую область видимости (и не должны конфликтовать с другими именами, объявленными в ней).
int main() { union { int a; const char* p; }; a = 1; p = "Jennifer"; }
` и `` оставлен без изменений, как и требовалось. HTML-разметка полностью сохранена.
Анонимные объединения в области видимости пространства имен должны быть объявлены static если они не находятся в безымянном пространстве имен.
Классы, подобные объединениям
A union-like class — это либо объединение (union), либо (не объединение) класс, который имеет по крайней мере одну анонимную структуру объединения в качестве члена. Union-like class имеет набор variant members :
- нестатические элементы данных его членов анонимных объединений;
- кроме того, если класс, подобный объединению, является объединением, его нестатические элементы данных, которые не являются анонимными объединениями.
Классы, подобные объединениям, могут использоваться для реализации tagged union .
#include <iostream> // S имеет одно нестатическое поле данных (tag), три элемента перечисления (CHAR, INT, DOUBLE), // и три вариантных члена (c, i, d) struct S { enum{CHAR, INT, DOUBLE} tag; union { char c; int i; double d; }; }; void print_s(const S& s) { switch(s.tag) { case S::CHAR: std::cout << s.c << '\n'; break; case S::INT: std::cout << s.i << '\n'; break; case S::DOUBLE: std::cout << s.d << '\n'; break; } } int main() { S s = {S::CHAR, 'a'}; print_s(s); s.tag = S::INT; s.i = 123; print_s(s); }
Вывод:
a 123
|
Стандартная библиотека C++ включает std::variant , который может заменить многие случаи использования объединений и классов, подобных объединениям. Приведённый выше пример может быть переписан как
Запустить этот код
#include <iostream> #include <variant> int main() { std::variant<char, int, double> s = 'a'; std::visit([](auto x){ std::cout << x << '\n';}, s); s = 123; std::visit([](auto x){ std::cout << x << '\n';}, s); } Вывод: a 123 |
(начиная с C++17) |
Ключевые слова
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1940 | C++11 | анонимные объединения допускали только нестатические члены данных |
static_assert
также разрешён
|
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
-
- 11.5 Объединения [class.union]
- Стандарт C++20 (ISO/IEC 14882:2020):
-
- 11.5 Объединения [class.union]
- Стандарт C++17 (ISO/IEC 14882:2017):
-
- 12.3 Объединения [class.union]
- Стандарт C++14 (ISO/IEC 14882:2014):
-
- 9.5 Объединения [class.union]
- Стандарт C++11 (ISO/IEC 14882:2011):
-
- 9.5 Объединения [class.union]
- Стандарт C++03 (ISO/IEC 14882:2003):
-
- 9.5 Объединения [class.union]
- Стандарт C++98 (ISO/IEC 14882:1998):
-
- 9.5 Объединения [class.union]
Смотрите также
|
(C++17)
|
типобезопасное размеченное объединение
(шаблон класса) |
|
C documentation
для
Объявление union
|
|