Enumeration declaration
Eine Enumeration ist ein eigener Typ, dessen Wert auf einen Wertebereich beschränkt ist (siehe unten für Details), der mehrere explizit benannte Konstanten (" Enumeratoren ") enthalten kann.
Die Werte der Konstanten sind Werte eines ganzzahligen Typs, der als der zugrundeliegende Typ der Aufzählung bekannt ist. Eine Aufzählung hat dieselbe Größe , Wertdarstellung und Ausrichtungsanforderungen wie ihr zugrundeliegender Typ. Darüber hinaus hat jeder Wert einer Aufzählung dieselbe 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 Folge beliebig vieler Attribute | ||||
| enum-head-name | - |
|
||||
| enum-base | - |
(seit C++11)
Doppelpunkt (
:
), gefolgt von einem
type-specifier-seq
, der einen integralen Typ benennt (falls er cv-qualifiziert ist, werden Qualifikationen ignoriert), der als fester zugrundeliegender Typ für diesen Enumerationstyp dient
|
||||
| enumerator-list | - |
kommagetrennte Liste von Enumeratordefinitionen, von denen jede entweder einfach ein eindeutiger
identifier
ist, der zum Namen des Enumerators wird, oder ein eindeutiger Bezeichner mit einem konstanten Ausdruck:
identifier
=
constant-expression
.
In beiden Fällen kann der
identifier
direkt von einer optionalen
Attributspezifizierer-Sequenz
gefolgt werden.
(seit C++17)
|
Es gibt zwei verschiedene Arten von Aufzählungen:
unbegrenzte Aufzählung
(deklariert mit dem
enum-key
enum
) und
begrenzte Aufzählung
(deklariert mit dem
enum-key
enum class
oder
enum struct
).
Inhaltsverzeichnis |
Unbegrenzte 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 Konstante des Enumerationstyps (d.h. name ), sichtbar im umschließenden Gültigkeitsbereich, und kann verwendet werden, wann immer Konstanten erforderlich sind.
Jeder Enumerator ist mit einem Wert des zugrundeliegenden Typs assoziiert. Wenn
=
in einer
enumerator-list
angegeben werden, werden die Werte der Enumeratoren durch die zugehörigen
constant-expression
s definiert. Wenn der erste Enumerator kein
=
besitzt, ist der assoziierte Wert null. Für jeden anderen Enumerator, dessen Definition kein
=
aufweist, ist der assoziierte Wert der Wert des vorherigen Enumerators 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 unbegrenzten Enumeration kann weggelassen werden: Eine solche Deklaration führt nur die Enumeratoren in den einschließenden Gültigkeitsbereich ein:
enum { a, b, c = 0, d = a + 2 }; // definiert a = 0, b = 1, c = 0, d = 2
Wenn eine unbegrenzte Aufzählung ein Klassenmitglied ist, können ihre Enumeratoren mit den Klassenmitglied-Zugriffsoperatoren
.
und
->
zugegriffen werden:
struct X { enum direction { left = 'l', right = 'r' }; }; X x; X* p = &x; int a = X::direction::left; // nur in C++11 und später erlaubt int b = X::left; int c = x.left; int d = p->left;
|
In den Deklarationsspezifizierern einer Memberdeklaration wird die Sequenz
immer als Teil einer Aufzählungsdeklaration geparst: struct S { enum E1 : int {}; enum E1 : int {}; // Fehler: Neudeklaration der Aufzählung, // NICHT geparst als Bitfeld der Länge Null vom Typ enum E1 }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int' wird NICHT als zugrundeliegender Typ geparst } |
(seit C++11) |
Aufzählungsname für Verknüpfungszwecke
Eine unbenannte Enumeration, die keinen typedef-Namen für Verknüpfungszwecke besitzt und einen Enumerator hat, wird für Verknüpfungszwecke durch ihren zugrundeliegenden Typ und ihren ersten Enumerator bezeichnet; eine solche Enumeration wird als einen Enumerator als Namen für Verknüpfungszwecke besitzend bezeichnet.
Bereichsbezogene Aufzählungen
1)
deklariert einen Bereichs-Aufzählungstyp, dessen zugrundeliegender Typ
int
ist (die Schlüsselwörter
class
und
struct
sind exakt äquivalent)
2)
deklariert einen Bereichs-Aufzählungstyp, dessen zugrundeliegender Typ
type
ist
3)
opake Enum-Deklaration für eine Bereichs-Aufzählung, deren zugrundeliegender Typ
int
ist
4)
opake Enum-Deklaration für eine Bereichs-Aufzählung, deren zugrundeliegender Typ
type
ist
Jeder
enumerator
wird zu einer benannten Konstante des Aufzählungstyps (d.h.
name
), die innerhalb des Gültigkeitsbereichs der Aufzählung enthalten ist und mit dem Bereichsauflösungsoperator zugänglich ist. Es gibt keine impliziten Konvertierungen von den Werten eines Bereichs-Aufzählers zu integralen Typen, obwohl
Code ausführen
#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; // Fehler: keine implizite Konvertierung von Bereichs-Enum zu int int n = static_cast<int>(r); // OK, n = 21 std::cout << n << '\n'; // gibt 21 aus } |
(seit C++11) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Eine Enumeration kann ohne Cast von einem Integer initialisiert werden, unter Verwendung von Listeninitialisierung , wenn alle folgenden Bedingungen erfüllt sind:
Dies ermöglicht die Einführung neuer Integertypen (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ählernamen der benannten Aufzählung ein, als ob durch eine using -Deklaration für jeden Aufzähler. Im Klassenbereich fügt eine using enum -Deklaration die Aufzähler der benannten Aufzählung als Mitglieder zum Bereich hinzu, wodurch sie für die Mitgliedersuche zugänglich werden. enum class fruit { orange, apple }; struct S { using enum fruit; // OK: führt orange und apple in S ein }; void f() { S s; s.orange; // OK: benennt fruit::orange S::orange; // OK: benennt fruit::orange } Zwei using enum -Deklarationen, die zwei Aufzähler mit demselben Namen einführen, stehen im Konflikt. enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // Fehler: color::orange und fruit::orange stehen im Konflikt } |
(seit C++20) | ||||||||||||||||||||||||||
Hinweise
Werte von Aufzählungstypen ohne Gültigkeitsbereich können zu integralen Typen heraufgestuft oder konvertiert werden:
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
Werte von Integer-, 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 für die Aufzählung definierten benannten Enumeratoren entsprechen muss:
enum access_t { read = 1, write = 2, exec = 4 }; // Enumeratoren: 1, 2, 4 Bereich: 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); // Undefiniertes Verhalten seit CWG 1766 access_t y = static_cast<access_t>(8); // Undefiniertes Verhalten seit CWG 1766 enum foo { a = 0, b = UINT_MAX }; // Bereich: [0, UINT_MAX] foo x = foo(-1); // Undefiniertes Verhalten seit CWG 1766, // selbst wenn der zugrundeliegende Typ von foo unsigned int ist
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_enumerator_attributes
|
201411L
|
(C++17) | Attribute für Enumeratoren |
__cpp_using_enum
|
201907L
|
(C++20) |
using enum
|
Schlüsselwörter
Beispiel
#include <cstdint> #include <iostream> // Enumeration, die 16 Bits belegt enum smallenum: std::int16_t { a, b, c }; // Farbe kann rot (Wert 0), gelb (Wert 1), grün (Wert 20) oder blau (Wert 21) sein enum color { red, yellow, green = 20, blue }; // Höhe kann altitude::high oder altitude::low sein enum class altitude: char { high = 'h', low = 'l', // Nachgestelltes Komma nur erlaubt nach CWG 518 }; // Die Konstante d ist 0, die Konstante e ist 1, die Konstante f ist 3 enum { d, e, f = e + 2 }; // Aufzählungstypen (sowohl scoped als auch unscoped) können überladene Operatoren haben 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); } // Der scoped enum (C++11) kann in früheren C++-Revisionen teilweise emuliert werden: enum struct E11 { x, y }; // seit 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 = rot a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
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 377 | C++98 |
das Verhalten war nicht spezifiziert, wenn kein integraler
Typ alle Enumeratorwerte darstellen kann |
die Enumeration ist in diesem Fall
fehlerhaft |
| CWG 518 | C++98 | ein nachgestelltes Komma war nach der Enumeratorliste nicht erlaubt | erlaubt |
| CWG 1514 | C++11 |
eine Neudefinition einer Enumeration mit festgelegtem zugrunde liegenden Typ
konnte als Bitfeld in einer Klassenmember-Deklaration geparst werden |
immer als Neudefinition geparst |
| CWG 1638 | C++11 |
Grammatik der opaken Enumerationsdeklaration
verbot die Verwendung für Template-Spezialisierungen |
Nested-Name-Specifier
erlaubt |
| CWG 1766 | C++98 |
das Konvertieren eines Wertes außerhalb des Bereichs in eine Enumeration
ohne festgelegten zugrunde liegenden Typ hatte ein nicht spezifiziertes Ergebnis |
das Verhalten ist undefiniert |
| CWG 1966 | C++11 |
die Lösung von
CWG Issue 1514
machte den
:
eines bedingten Ausdrucks Teil von enum-base |
wende die Lösung nur auf
Member-Deklarationsspezifizierer an |
| CWG 2156 | C++11 |
Enum-Definitionen konnten Enumerationstypen
durch using-Deklarationen definieren |
verboten |
| CWG 2157 | C++11 |
die Lösung von
CWG Issue 1966
deckte
keine qualifizierten Enumerationsnamen ab |
abgedeckt |
| CWG 2530 | C++98 |
eine Enumeratorliste konnte mehrere
Enumeratoren mit demselben Bezeichner enthalten |
verboten |
| CWG 2590 | C++98 |
die Größe, Wertdarstellung und Ausrichtungsanforderungen
einer Enumeration hingen nicht von ihrem zugrunde liegenden Typ ab |
alle sind identisch mit
denen des zugrunde liegenden Typs |
| CWG 2621 | C++20 |
die in
using enum Deklarationen verwendete Enumerationsnamensuche war unklar |
klargestellt |
| CWG 2877 | C++20 |
die in
using enum Deklarationen verwendete Enumerationsnamensuche war nicht typ-only |
zu typ-only gemacht |
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 Aufzählungsdeklarationen [dcl.enum]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 10.2 Aufzählungsdeklarationen [dcl.enum]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 7.2 Aufzählungsdeklarationen [dcl.enum]
- C++11-Standard (ISO/IEC 14882:2011):
-
- 7.2 Aufzählungsdeklarationen [dcl.enum]
- C++03-Standard (ISO/IEC 14882:2003):
-
- 7.2 Aufzählungsdeklarationen [dcl.enum]
- C++98-Standard (ISO/IEC 14882:1998):
-
- 7.2 Aufzählungsdeklarationen [dcl.enum]
Siehe auch
|
(C++11)
|
prüft, ob ein Typ ein Aufzählungstyp ist
(Klassentemplate) |
|
(C++23)
|
prüft, ob ein Typ ein scoped enumeration type ist
(Klassentemplate) |
|
(C++11)
|
ermittelt den zugrundeliegenden Integer-Typ für einen gegebenen Aufzählungstyp
(Klassentemplate) |
|
(C++23)
|
konvertiert eine Aufzählung in ihren zugrundeliegenden Typ
(Funktionstemplate) |
|
C-Dokumentation
für
Aufzählungen
|
|