Spec-Zone.ru › C++

Объявление объединения

Объединение — это специальный тип класса, который может одновременно содержать только один из своих нестатических членов-данных.

Синтаксис

Спецификатор класса для объявления объединения похож на объявление класса или структуры:

union attr (необязательно) class-head-name (необязательно) { member-specification }
attr - (начиная с C++11) последовательность из любого числа атрибутов
class-head-name - имя определяемого объединения. Перед ним может указываться nested-name-specifier (последовательность имён и операторов разрешения области видимости, заканчивающаяся оператором разрешения области видимости). Имя можно опустить; в этом случае объединение является безымянным
member-specification - список спецификаторов доступа, объявлений и определений объектов-членов и функций-членов.

Объединение может иметь функции-члены (в том числе конструкторы и деструкторы), но не виртуальные функции.

Объединение не может иметь базовых классов и не может использоваться в качестве базового класса.

Не более одного вариантного члена может иметь инициализатор члена по умолчанию.

(начиная с C++11)

Объединение не может иметь нестатические члены-данные ссылочного типа.

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

(до C++11)

Если объединение содержит нестатический член-данные с нетривиальной специальной функцией-членом, соответствующая специальная функция-член объединения может быть определена как удалённая; подробности см. на странице соответствующей специальной функции-члена.

(начиная с C++11)

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

Пояснение

Объединение имеет размер, достаточный для хранения самого большого члена-данного, но обычно не превышающий необходимый. Предполагается, что остальные члены-данные размещаются в тех же байтах, что и этот самый большой член. Детали такого размещения определяются реализацией, за исключением того, что все нестатические члены-данные имеют один и тот же адрес. Чтение члена объединения, в который последним не выполнялась запись, приводит к неопределённому поведению. Многие компиляторы реализуют возможность чтения неактивных членов объединения как нестандартное расширение языка.

#include <cstdint>
#include <iostream>

union S
{
    std::int32_t n;     // occupies 4 bytes
    std::uint16_t s[2]; // occupies 4 bytes
    std::uint8_t c;     // occupies 1 byte
};                      // the whole union occupies 4 bytes

int main()
{
    S s = {0x12345678}; // initializes the first member, s.n is now the active member
    // At this point, reading from s.s or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << std::hex << "s.n = " << s.n << '\n';

    s.s[0] = 0x0011; // s.s is now the active member
    // At this point, reading from s.n or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << "s.c is now " << +s.c << '\n' // 11 or 00, depending on platform
              << "s.n is now " << s.n << '\n'; // 12340011 or 00115678
}

Возможный вывод:

s.n = 12345678
s.c is now 0
s.n is now 115678

Каждый член размещается так, как если бы он был единственным членом класса.

Если члены объединения являются классами с определёнными пользователем конструкторами и деструкторами, для переключения активного члена обычно требуются явный вызов деструктора и placement new:

#include <iostream>
#include <string>
#include <vector>

union S
{
    std::string str;
    std::vector<int> vec;
    ~S() {} // needs to know which member is active, only possible in union-like class 
};          // the whole union occupies max(sizeof(string), sizeof(vector<int>))

int main()
{
    S s = {"Hello, world"};
    // at this point, reading from s.vec is undefined behavior
    std::cout << "s.str = " << s.str << '\n';
    s.str.~basic_string();
    new (&s.vec) std::vector<int>;
    // now, s.vec is the active member of the union
    s.vec.push_back(10);
    std::cout << s.vec.size() << '\n';
    s.vec.~vector();
}

Вывод:

s.str = Hello, world
1
(начиная с C++11)

Если два члена объединения являются типами со стандартной компоновкой, на любом компиляторе разрешено исследовать их общую последовательность.

Время жизни члена

Время жизни члена объединения начинается, когда этот член становится активным. Если ранее был активен другой член, его время жизни завершается.

Когда активный член объединения переключается оператором присваивания вида 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;               // does not start lifetime of any union member
    c.b.a.y[3] = 4;    // OK: "c.b.a.y[3]", names union members c.b and c.b.a.y;
                       // This creates objects to hold union members c.b and c.b.a.y
    return c.b.a.y[3]; // OK: c.b.a.y refers to newly created object
}

struct X { const int a; int b; };
union Y { X x; int k; };

void g()
{
    Y y = {{1, 2}}; // OK, y.x is active union member
    int n = y.x.a;
    y.k = 4;   // OK: ends lifetime of y.x, y.k is active member of union
    y.x.b = n; // undefined behavior: y.x.b modified outside its lifetime,
               // "y.x.b" names y.x, but X's default constructor is deleted,
               // so union member y.x's lifetime does not implicitly start
}

Тривиальные конструктор перемещения, оператор присваивания перемещением, (начиная с C++11)конструктор копирования и оператор присваивания копированием типов объединений копируют представления объектов. Если источник и назначение не являются одним и тем же объектом, эти специальные функции-члены начинают время жизни каждого объекта, вложенного в объект назначения и соответствующего объекту, вложенному в источник, до выполнения копирования. В противном случае они ничего не делают. После конструирования или присваивания с помощью тривиальных специальных функций-членов два объекта-объединения имеют один и тот же соответствующий активный член (если он есть).

Безымянные объединения

Безымянное объединение — это определение безымянного объединения, которое одновременно не определяет никаких переменных (включая объекты типа объединения, ссылки или указатели на объединение).

union { member-specification } ;

Для безымянных объединений действуют дополнительные ограничения: они не могут иметь функции-члены и статические члены-данные, а все их члены-данные должны быть открытыми. Разрешены только объявления нестатических членов-данных и static_assert(начиная с C++11).

Члены безымянного объединения вводятся в окружающую область видимости (и не должны конфликтовать с другими объявленными там именами).

int main()
{
    union
    {
        int a;
        const char* p;
    };
    a = 1;
    p = "Jennifer";
}

Безымянные объединения в области видимости пространства имён должны объявляться как static, если только они не находятся в безымянном пространстве имён.

Классы, подобные объединениям

Класс, подобный объединению, — это либо объединение, либо класс (не являющийся объединением), который имеет хотя бы одно безымянное объединение в качестве члена. Класс, подобный объединению, имеет набор вариантных членов :

  • нестатические члены-данные его безымянных объединений-членов;
  • а также, если класс, подобный объединению, сам является объединением, его нестатические члены-данные, не являющиеся безымянными объединениями.

Классы, подобные объединениям, можно использовать для реализации размеченного объединения.

#include <iostream>

// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), 
// and three variant members (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)

Ключевые слова

union

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам 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]

См. также

variant
(C++17)
типобезопасное размеченное объединение
(шаблон класса)
Документация по C о объявлении объединения

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/union

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API