Namespaces
Variants

Union declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Eine Union ist ein spezieller Klassentyp, der zu jedem Zeitpunkt nur eines seiner nicht-statischen Datenmitglieder halten kann.

Inhaltsverzeichnis

Syntax

Die Klassenspezifikation für eine Union-Deklaration ähnelt der class- oder struct Deklaration:

union attr class-head-name { member-specification }
attr - (since C++11) optionale Sequenz beliebig vieler attributes
class-head-name - der Name der zu definierenden Union. Optional vorangestellt durch nested-name-specifier (Sequenz von Namen und Bereichsauflösungsoperatoren, endend mit einem Bereichsauflösungsoperator). Der Name kann weggelassen werden, in diesem Fall ist die Union unbenannt
member-specification - Liste von Zugriffsspezifizierern, Member-Objekt- und Member-Funktionsdeklarationen und -definitionen.

Eine Union kann Elementfunktionen (einschließlich Konstruktoren und Destruktoren) haben, aber keine virtuellen Funktionen.

Eine Union kann keine Basisklassen haben und kann nicht als Basisklasse verwendet werden.

Höchstens ein Variantenmitglied kann einen Standard-Member-Initialisierer besitzen.

(seit C++11)

Eine Union kann keine nicht-statischen Datenelemente von Referenztypen haben.

Unions dürfen kein nicht-statisches Datenelement mit einer nicht-trivialen speziellen Elementfunktion enthalten.

(bis C++11)

Wenn eine Union ein nicht-statisches Datenelement mit einer nicht-trivialen speziellen Elementfunktion enthält, kann die entsprechende spezielle Elementfunktion der Union als gelöscht definiert sein, siehe die entsprechende Seite für spezielle Elementfunktionen für Details.

(seit C++11)

Genau wie bei der struct -Deklaration ist der standardmäßige Mitgliedszugriff in einer union public .

Erklärung

Die Union ist mindestens so groß wie nötig, um ihr größtes Datenelement zu halten, aber normalerweise nicht größer. Die anderen Datenelemente sollen in denselben Bytes als Teil dieses größten Elements allokiert werden. Die Details dieser Allokation sind implementierungsdefiniert, mit der Ausnahme, dass alle nicht-statischen Datenelemente dieselbe Adresse haben. Es ist undefiniertes Verhalten, aus dem Element der Union zu lesen, das nicht zuletzt geschrieben wurde. Viele Compiler implementieren als nicht-standardisierte Spracherweiterung die Fähigkeit, inaktive Elemente einer Union zu lesen.

#include <cstdint>
#include <iostream>
union S
{
    std::int32_t n;     // belegt 4 Bytes
    std::uint16_t s[2]; // belegt 4 Bytes
    std::uint8_t c;     // belegt 1 Byte
};                      // die gesamte Union belegt 4 Bytes
int main()
{
    S s = {0x12345678}; // initialisiert das erste Element, s.n ist jetzt das aktive Element
    // An diesem Punkt ist das Lesen von s.s oder s.c undefiniertes Verhalten,
    // aber die meisten Compiler definieren es.
    std::cout << std::hex << "s.n = " << s.n << '\n';
    s.s[0] = 0x0011; // s.s ist jetzt das aktive Element
    // An diesem Punkt ist das Lesen von s.n oder s.c undefiniertes Verhalten,
    // aber die meisten Compiler definieren es.
    std::cout << "s.c is now " << +s.c << '\n' // 11 oder 00, abhängig von der Plattform
              << "s.n is now " << s.n << '\n'; // 12340011 oder 00115678
}

Mögliche Ausgabe:

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

Jedes Mitglied wird so allokiert, als wäre es das einzige Mitglied der Klasse.

Wenn Mitglieder einer Union Klassen mit benutzerdefinierten Konstruktoren und Destruktoren sind, sind zum Wechseln des aktiven Mitglieds im Allgemeinen explizite Destruktoren und Placement New erforderlich:

#include <iostream>
#include <string>
#include <vector>
union S
{
    std::string str;
    std::vector<int> vec;
    ~S() {} // muss wissen, welches Mitglied aktiv ist, nur in union-ähnlichen Klassen möglich 
};          // die gesamte Union belegt max(sizeof(string), sizeof(vector<int>))
int main()
{
    S s = {"Hello, world"};
    // an diesem Punkt ist das Lesen von s.vec undefiniertes Verhalten
    std::cout << "s.str = " << s.str << '\n';
    s.str.~basic_string();
    new (&s.vec) std::vector<int>;
    // jetzt ist s.vec das aktive Mitglied der Union
    s.vec.push_back(10);
    std::cout << s.vec.size() << '\n';
    s.vec.~vector();
}

Ausgabe:

s.str = Hello, world
1
(seit C++11)

Wenn zwei Union-Mitglieder Standard-Layout -Typen sind, ist es wohldefiniert, ihre gemeinsame Teilsequenz auf jedem Compiler zu untersuchen.

Lebensdauer von Mitgliedern

Die Lebensdauer eines Union-Mitglieds beginnt, wenn das Mitglied aktiviert wird. Falls zuvor ein anderes Mitglied aktiv war, endet dessen Lebensdauer.

Wenn das aktive Mitglied einer Union durch einen Zuweisungsausdruck der Form E1 = E2 umgeschaltet wird, der entweder den eingebauten Zuweisungsoperator oder einen trivialen Zuweisungsoperator verwendet, wird für jedes Unionsmitglied X, das in den Mitgliedszugriffs- und Arrayindex-Teilausdrücken von E1 vorkommt und keine Klasse mit nicht-trivialen oder gelöschten Standardkonstruktoren ist, wenn die Modifikation von X nach den Typ-Aliasing-Regeln undefiniertes Verhalten hätte, ein Objekt vom Typ von X implizit im nominierten Speicher erzeugt; es wird keine Initialisierung durchgeführt und der Beginn seiner Lebensdauer wird nach der Wertberechnung des linken und rechten Operanden und vor der Zuweisung eingeordnet.

union A { int x; int y[4]; };
struct B { A a; };
union C { B b; int k; };
int f()
{
    C c;               // startet die Lebensdauer keines Union-Mitglieds
    c.b.a.y[3] = 4;    // OK: "c.b.a.y[3]" benennt Union-Mitglieder c.b und c.b.a.y;
                       // Dies erzeugt Objekte, um die Union-Mitglieder c.b und c.b.a.y zu halten
    return c.b.a.y[3]; // OK: c.b.a.y referenziert das neu erstellte Objekt
}
struct X { const int a; int b; };
union Y { X x; int k; };
void g()
{
    Y y = {{1, 2}}; // OK, y.x ist aktives Union-Mitglied
    int n = y.x.a;
    y.k = 4;   // OK: beendet die Lebensdauer von y.x, y.k ist aktives Mitglied der Union
    y.x.b = n; // undefiniertes Verhalten: y.x.b wurde außerhalb seiner Lebensdauer modifiziert,
               // "y.x.b" benennt y.x, aber Xs Standardkonstruktor ist gelöscht,
               // daher startet die Lebensdauer des Union-Mitglieds y.x nicht implizit
}

Trivialer Move-Konstruktor, Move-Zuweisungsoperator, (seit C++11) Kopierkonstruktor und Kopierzuweisungsoperator von Union-Typen kopieren Objektrepräsentationen. Wenn Quelle und Ziel nicht dasselbe Objekt sind, beginnen diese speziellen Memberfunktionen die Lebensdauer jedes Objekts (außer für Objekte, die weder Unterobjekte des Ziels noch von implicit-lifetime type sind), das im Ziel dem im Quellobjekt verschachtelten Objekt entspricht, bevor der Kopiervorgang durchgeführt wird. Andernfalls tun sie nichts. Zwei Union-Objekte haben nach der Konstruktion oder Zuweisung durch triviale spezielle Funktionen dasselbe entsprechende aktive Member (falls vorhanden).

Anonyme Unions

Eine anonyme Union ist eine unbenannte Union-Definition, die nicht gleichzeitig Variablen definiert (einschließlich Objekte des Union-Typs, Referenzen oder Zeiger auf die Union).

union { Mitglieder-Spezifikation } ;

Anonyme Unions haben weitere Einschränkungen: Sie können keine Elementfunktionen haben, keine statischen Datenelemente haben, und alle ihre Datenelemente müssen öffentlich sein. Die einzigen erlaubten Deklarationen sind nicht-statische Datenelemente und static_assert Deklarationen (seit C++11) .

Mitglieder einer anonymen Union werden in den umschließenden Gültigkeitsbereich eingefügt (und dürfen nicht mit anderen dort deklarierten Namen in Konflikt stehen).

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

Anonyme Unions im Namespace-Bereich müssen als static deklariert werden, es sei denn, sie befinden sich in einem unbenannten Namespace.

Union-ähnliche Klassen

Eine union-ähnliche Klasse ist entweder eine Union oder eine (Nicht-Union-)Klasse, die mindestens eine anonyme Union als Mitglied hat. Eine union-ähnliche Klasse hat eine Menge von Varianten-Mitgliedern  :

  • die nicht-statischen Datenelemente seiner Mitglieder anonymen Unions;
  • zusätzlich, wenn die union-ähnliche Klasse eine Union ist, ihre nicht-statischen Datenelemente, die keine anonymen Unions sind.

Union-ähnliche Klassen können zur Implementierung von tagged union verwendet werden.

#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);
}

Ausgabe:

a
123

Die C++-Standardbibliothek enthält std::variant , die viele Verwendungen von Unions und union-ähnlichen Klassen ersetzen kann. Das obige Beispiel kann wie folgt umgeschrieben werden:

#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);
}

Ausgabe:

a
123
(seit C++17)

Schlüsselwörter

union

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 1940 C++11 anonyme Unions erlaubten nur nicht-statische Datenelemente static_assert ebenfalls erlaubt

Referenzen

  • C++23-Standard (ISO/IEC 14882:2024):
  • 11.5 Unions [class.union]
  • C++20-Standard (ISO/IEC 14882:2020):
  • 11.5 Unions [class.union]
  • C++17-Standard (ISO/IEC 14882:2017):
  • 12.3 Unions [class.union]
  • C++14 Standard (ISO/IEC 14882:2014):
  • 9.5 Unions [class.union]
  • C++11 Standard (ISO/IEC 14882:2011):
  • 9.5 Unions [class.union]
  • C++03 Standard (ISO/IEC 14882:2003):
  • 9.5 Unions [class.union]
  • C++98 Standard (ISO/IEC 14882:1998):
  • 9.5 Unions [class.union]

Siehe auch

(C++17)
eine typsichere diskriminierte Union
(Klassentemplate)
C-Dokumentation für Union-Deklaration