Enumeration declaration
Eine Aufzählung ist ein eigener Typ, dessen Werte auf einen Wertebereich beschränkt sind (Details siehe unten), der mehrere explizit benannte Konstanten („Aufzählungswerte“) enthalten kann.
Die Werte der Konstanten sind Werte eines ganzzahligen Typs, der als zugrundeliegender Typ der Aufzählung bekannt ist. Eine Aufzählung hat die gleiche Größe, Wertdarstellung und Ausrichtungsvoraussetzungen wie ihr zugrundeliegender Typ. Darüber hinaus hat jeder Wert einer Aufzählung die gleiche Darstellung wie der entsprechende Wert des zugrundeliegenden Typs.
Eine Aufzählung wird mit der folgenden Syntax (neu) deklariert:
enum-key attr (optional) enum-head-name (optional) enum-base (optional){ enumerator-list (optional) } |
(1) | ||||||||
enum-key attr (optional) enum-head-name (optional) enum-base (optional){ enumerator-list , } |
(2) | ||||||||
enum-key attr (optional) enum-head-name enum-base (optional) ; |
(3) | (seit C++11) | |||||||
| enum-key | - |
| ||||
| attr | - | (seit C++11) optionale Sequenz beliebig vieler Attribute | ||||
| enum-head-name | - |
| ||||
| enum-base | - | (seit C++11) Ein Doppelpunkt (:), gefolgt von einer type-specifier-seq, die einen ganzzahligen Typ benennt (wenn er cv-qualifiziert ist, werden die Qualifizierer ignoriert), der als fester zugrundeliegender Typ für diesen Aufzählungstyp dient. | ||||
| enumerator-list | - | eine durch Kommas getrennte Liste von Aufzählungswerte-Definitionen, die jeweils entweder einfach ein eindeutiger identifier ist, der zum Namen des Aufzählungswerts wird, oder ein eindeutiger Bezeichner mit einem konstanten Ausdruck: identifier = constant-expression. In beiden Fällen kann der identifier optional von einer Attributspezifikationssequenz gefolgt werden.(seit C++17) |
Es gibt zwei verschiedene Arten von Aufzählungen: nicht-scoped Aufzählungen (deklariert mit dem enum-key enum) und scoped Aufzählungen (deklariert mit dem enum-key enum class oder enum struct).
Inhalt |
[bearbeiten] Nicht-scoped Aufzählungen
enum name (optional) { enumerator = constant-expression , enumerator = constant-expression , ... } |
(1) | ||||||||
enum name (optional) : type { enumerator = constant-expression , enumerator = constant-expression , ... } |
(2) | (seit C++11) | |||||||
enum name : type ; |
(3) | (seit C++11) | |||||||
Jeder enumerator wird zu einer benannten Konstanten des Aufzählungstyps (d. h. name), die im umschließenden Geltungsbereich sichtbar ist und immer dann verwendet werden kann, wenn Konstanten benötigt werden.
Jeder Aufzählungswert wird einem Wert des zugrundeliegenden Typs zugeordnet. Wenn = in einer enumerator-list angegeben ist, werden die Werte der Aufzählungswerte durch die zugehörigen constant-expression definiert. Wenn der erste Aufzählungswert kein = hat, ist der zugehörige Wert null. Für jeden anderen Aufzählungswert, dessen Definition kein = hat, ist der zugehörige Wert der Wert des vorherigen Aufzählungswerts plus eins.
enum Foo { a, b, c = 10, d, e = 1, f, g = f + c }; //a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12
Der name einer nicht-scoped Aufzählung kann weggelassen werden: Eine solche Deklaration führt lediglich die Aufzählungswerte in den umschließenden Geltungsbereich ein.
enum { a, b, c = 0, d = a + 2 }; // defines a = 0, b = 1, c = 0, d = 2
Wenn eine nicht-scoped Aufzählung ein Klassenmitglied ist, können ihre Aufzählungswerte mit den Member-Zugriffsoperatoren . und -> zugegriffen werden.
struct X { enum direction { left = 'l', right = 'r' }; }; X x; X* p = &x; int a = X::direction::left; // allowed only in C++11 and later int b = X::left; int c = x.left; int d = p->left;
|
In den Deklarationsspezifizierern einer Mitgliedsdeklaration wird die Sequenz
immer als Teil einer Aufzählungsdeklaration interpretiert. struct S { enum E1 : int {}; enum E1 : int {}; // error: redeclaration of enumeration, // NOT parsed as a zero-length bit-field of type enum E1 }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int' is NOT parsed as the underlying type } |
(seit C++11) |
[bearbeiten] Aufzählungsname für Linkage-Zwecke
Eine unbenannte Aufzählung, die keinen typedef-Namen für Linkage-Zwecke hat und einen Aufzählungswert besitzt, wird für Linkage-Zwecke durch ihren zugrundeliegenden Typ und ihren ersten Aufzählungswert bezeichnet; eine solche Aufzählung hat einen Aufzählungswert als Namen für Linkage-Zwecke.
[bearbeiten] Scoped Aufzählungen
1) Deklariert einen scoped Aufzählungstyp, dessen zugrundeliegender Typ int ist (die Schlüsselwörter class und struct sind exakt äquivalent)
2) Deklariert einen scoped Aufzählungstyp, dessen zugrundeliegender Typ type ist.
3) Opaque enum declaration für eine scoped Aufzählung, deren zugrundeliegender Typ int ist.
4) Opaque enum declaration für eine scoped Aufzählung, deren zugrundeliegender Typ type ist.
Jeder enumerator wird zu einer benannten Konstanten des Aufzählungstyps (d. h. name), die sich im Geltungsbereich der Aufzählung befindet und mit dem Scope-Auflösungsoperator zugegriffen werden kann. Es gibt keine impliziten Konvertierungen von den Werten eines scoped Aufzählungswerts in ganzzahlige Typen, obwohl Führen Sie diesen Code aus #include <iostream> int main() { enum class Color { red, green = 20, blue }; Color r = Color::blue; switch(r) { case Color::red : std::cout << "red\n"; break; case Color::green: std::cout << "green\n"; break; case Color::blue : std::cout << "blue\n"; break; } // int n = r; // error: no implicit conversion from scoped enum to int int n = static_cast<int>(r); // OK, n = 21 std::cout << n << '\n'; // prints 21 } |
(seit C++11) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Eine Aufzählung kann ohne Cast aus einer ganzen Zahl initialisiert werden, indem List-Initialisierung verwendet wird, wenn alle folgenden Bedingungen erfüllt sind:
Dies ermöglicht die Einführung neuer ganzzahliger Typen (z. B. enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17) byte b{42}; // OK as of C++17 (direct-list-initialization) byte c = {42}; // error byte d = byte{42}; // OK as of C++17; same value as b byte e{-1}; // error struct A { byte b; }; A a1 = {{42}}; // error (copy-list-initialization of a constructor parameter) A a2 = {byte{42}}; // OK as of C++17 void f(byte); f({42}); // error (copy-list-initialization of a function parameter) enum class Handle : std::uint32_t { Invalid = 0 }; Handle h{42}; // OK as of C++17 |
(seit C++17) |
using enum Deklaration
enum E { x }; void f() { int E; using enum E; // OK } using F = E; using enum F; // OK template<class T> using EE = T; void g() { using enum EE<E>; // OK } Eine using enum Deklaration führt die Aufzählungswerte des benannten Aufzählungstyps ein, als ob durch eine using Deklaration für jeden Aufzählungswert. Wenn sie im Klassenbereich (class scope) vorkommt, fügt eine using enum Deklaration die Aufzählungswerte der benannten Aufzählung als Mitglieder zum Bereich hinzu und macht sie für die Mitgliedssuche zugänglich. enum class fruit { orange, apple }; struct S { using enum fruit; // OK: introduces orange and apple into S }; void f() { S s; s.orange; // OK: names fruit::orange S::orange; // OK: names fruit::orange } Zwei using enum Deklarationen, die zwei Aufzählungswerte mit demselben Namen einführen, kollidieren. enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // error: color::orange and fruit::orange conflict } |
(seit C++20) | ||||||||||||||||||||||||||
[bearbeiten] Hinweise
Werte eines nicht-scoped Aufzählungstyps können hochgestuft oder in ganzzahlige Typen konvertiert werden.
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
Werte von ganzzahligen, Gleitkomma- und Aufzählungstypen können durch Verwendung von static_cast in jeden Aufzählungstyp konvertiert werden. Beachten Sie, dass der Wert nach einer solchen Konvertierung nicht notwendigerweise einem der benannten Aufzählungswerte entsprechen muss, die für die Aufzählung definiert wurden.
enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7 access_t rwe = static_cast<access_t>(7); assert((rwe & read) && (rwe & write) && (rwe & exec)); access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766 access_t y = static_cast<access_t>(8); // undefined behavior since CWG 1766 enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX] foo x = foo(-1); // undefined behavior since CWG 1766, // even if foo's underlying type is unsigned int
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_enumerator_attributes |
201411L |
(C++17) | Attribute für Aufzählungswerte |
__cpp_using_enum |
201907L |
(C++20) | using enum
|
[bearbeiten] Schlüsselwörter
[bearbeiten] Beispiel
#include <cstdint> #include <iostream> // enum that takes 16 bits enum smallenum: std::int16_t { a, b, c }; // color may be red (value 0), yellow (value 1), green (value 20), or blue (value 21) enum color { red, yellow, green = 20, blue }; // altitude may be altitude::high or altitude::low enum class altitude: char { high = 'h', low = 'l', // trailing comma only allowed after CWG 518 }; // the constant d is 0, the constant e is 1, the constant f is 3 enum { d, e, f = e + 2 }; // enumeration types (both scoped and unscoped) can have overloaded operators std::ostream& operator<<(std::ostream& os, color c) { switch(c) { case red : os << "red"; break; case yellow: os << "yellow"; break; case green : os << "green"; break; case blue : os << "blue"; break; default : os.setstate(std::ios_base::failbit); } return os; } std::ostream& operator<<(std::ostream& os, altitude al) { return os << static_cast<char>(al); } // The scoped enum (C++11) can be partially emulated in earlier C++ revisions: enum struct E11 { x, y }; // since C++11 struct E98 { enum { x, y }; }; // OK in pre-C++11 namespace N98 { enum { x, y }; } // OK in pre-C++11 struct S98 { static const int x = 0, y = 1; }; // OK in pre-C++11 void emu() { std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4 } namespace cxx20 { enum class long_long_long_name { x, y }; void using_enum_demo() { std::cout << "C++20 `using enum`: __cpp_using_enum == "; switch (auto rnd = []{return long_long_long_name::x;}; rnd()) { #if defined(__cpp_using_enum) using enum long_long_long_name; case x: std::cout << __cpp_using_enum << "; x\n"; break; case y: std::cout << __cpp_using_enum << "; y\n"; break; #else case long_long_long_name::x: std::cout << "?; x\n"; break; case long_long_long_name::y: std::cout << "?; y\n"; break; #endif } } } int main() { color col = red; altitude a; a = altitude::low; std::cout << "col = " << col << '\n' << "a = " << a << '\n' << "f = " << f << '\n'; cxx20::using_enum_demo(); }
Mögliche Ausgabe
col = red a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 377 | C++98 | das Verhalten war nicht spezifiziert, wenn kein ganzzahliger Typ alle Aufzählungswerte darstellen kann |
ist die Aufzählung fehler- formt in diesem Fall |
| CWG 518 | C++98 | ein nachfolgendes Komma war nach der Aufzählungswerte-Liste nicht erlaubt | erlaubt |
| CWG 1514 | C++11 | eine Neudefinition einer Aufzählung mit festem zugrundeliegendem Typ konnte als Bitfeld in einer Klassenmitgliedsdeklaration interpretiert werden |
wird immer als Neudefinition interpretiert |
| CWG 1638 | C++11 | Grammatik der opaken Aufzählungsdeklaration verbotene Verwendung für Templatespezialisierungen |
nested-name-specifier erlaubt |
| CWG 1766 | C++98 | das Umwandeln eines Werts außerhalb des gültigen Bereichs in eine Aufzählung ohne festen zugrundeliegenden Typ hatte ein nicht spezifiziertes Ergebnis |
das Verhalten ist undefiniert |
| CWG 1966 | C++11 | die Auflösung des CWG-Problems 1514 machte den :eines bedingten Ausdrucks Teil von enum-base |
wenden Sie die Auflösung nur auf Mitgliedsdeklarationsspezifizierer an |
| CWG 2156 | C++11 | Aufzählungsdefinitionen konnten Aufzählungstypen durch using-Deklarationen definieren |
verboten |
| CWG 2157 | C++11 | die Auflösung des CWG-Problems 1966 hat qualifizierte Aufzählungsnamen nicht abgedeckt |
abgedeckt |
| CWG 2530 | C++98 | eine Aufzählungswerte-Liste konnte mehrere Aufzählungswerte mit demselben Bezeichner enthalten |
verboten |
| CWG 2590 | C++98 | die Größe, Wertdarstellung und Ausrichtungsvoraussetzungen einer Aufzählung hingen nicht von ihrem zugrundeliegenden Typ ab |
alle von ihnen sind identisch mit denen des zugrundeliegenden Typs |
| CWG 2621 | C++20 | die Namenssuche für Aufzählungen, die in using enum Deklarationen verwendet wird, war unklar |
wurde klargestellt |
| CWG 2877 | C++20 | die Namenssuche für Aufzählungen, die in using enum Deklarationen waren nicht nur typbasiert |
wurde typbasiert gemacht |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 9.7.1 Enumeration declarations [dcl.enum]
- C++20 Standard (ISO/IEC 14882:2020)
- 9.7.1 Enumeration declarations [dcl.enum]
- C++17 Standard (ISO/IEC 14882:2017)
- 10.2 Enumeration declarations [dcl.enum]
- C++14 Standard (ISO/IEC 14882:2014)
- 7.2 Enumeration declarations [dcl.enum]
- C++11 Standard (ISO/IEC 14882:2011)
- 7.2 Enumeration declarations [dcl.enum]
- C++03-Standard (ISO/IEC 14882:2003)
- 7.2 Enumeration declarations [dcl.enum]
- C++98 Standard (ISO/IEC 14882:1998)
- 7.2 Enumeration declarations [dcl.enum]
[bearbeiten] Siehe auch
| (C++11) |
prüft, ob ein Typ ein Aufzählungstyp ist (Klassenvorlage) |
| (C++23) |
prüft, ob ein Typ ein namensgebender Aufzählungstyp ist (Klassenvorlage) |
| (C++11) |
erhält den zugrundeliegenden ganzzahligen Typ für einen gegebenen Aufzählungstyp (Klassenvorlage) |
| (C++23) |
konvertiert eine Aufzählung in ihren zugrundeliegenden Typ (Funktionsvorlage) |
| C-Dokumentation für Aufzählungen
| |