Union declaration
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:
Diesen Code ausführen
#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:
Diesen Code ausführen
#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
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
|
|