Enumerationen (C++)

Eine Aufzählung ist ein benutzerdefinierter Typ, der aus einer Gruppe benannter integraler Konstanten besteht, die als Enumeratoren bezeichnet werden.

Hinweis

In diesem Artikel werden der ISO-Standard-C++-Sprachtyp enum und der bereichsbezogene (oder stark typierte) enum class Typ behandelt, der in C++11 eingeführt wird. Informationen zu den public enum class Typen in C++/CLI und C++/CX finden Sie unter enum class (C++/CLI und C++/CX)private enum class.

Syntax

enum-name:
identifier

enum-specifier:
enum-head{enumerator-listopt}
enum-head { enumerator-list , }

enum-head:
enum-keyattribute-specifier-seqoptenum-head-nameoptenum-baseopt

enum-head-name:
nested-name-specifieroptidentifier

opaque-enum-declaration:
enum-keyattribute-specifier-seqoptenum-head-nameenum-baseopt;

enum-key:
enum
enum class
enum struct

enum-base:
: type-specifier-seq

enumerator-list:
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition:
enumerator
enumerator = constant-expression

enumerator:
identifierattribute-specifier-seqopt

Verwendung

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

Parameter

identifier
Der Typname, der für die Enumeration angegeben wurde.

type
Der zugrunde liegende Typ der Enumeratoren. Alle Enumeratoren weisen den gleichen zugrunde liegenden Typ auf. Kann ein beliebiger ganzzahliger Typ sein.

enum-list
Eine durch Trennzeichen getrennte Liste mit Enumeratoren in der Enumeration. Jeder Enumerator oder Variablenname im Bereich muss eindeutig sein. Allerdings können die Werte doppelt vorkommen. In einer nicht bereichsbezogenen Enumeration ist der Bereich der umgebende Bereich; in einer bereichsbezogenen Enumeration ist der Bereich der enum-list selbst. In einer bereichsbezogenen Enumeration kann die Liste leer sein, die tatsächlich einen neuen integralen Typ definiert.

class
Wenn Sie diese Schlüsselwort (keyword) in der Deklaration verwenden, geben Sie den Bereich der Enumeration an und identifier müssen angegeben werden. Sie können auch die struct Schlüsselwort (keyword) anstelle von class, da sie semantisch gleichwertig in diesem Kontext sind.

Enumerationsbereich

Eine Enumeration stellt einen Kontext bereit, um einen Wertebereich zu beschreiben, der als benannte Konstanten dargestellt wird. Diese benannten Konstanten werden auch als Enumeratoren bezeichnet. In den ursprünglichen C- und C++ enum -Typen sind die nicht qualifizierten Enumerationen im gesamten Bereich sichtbar, in dem die enum Deklaration erfolgt. In bereichsbezogenen Enumerationen muss der Enumeratorname durch den enum Typnamen qualifiziert werden. Das folgende Beispiel veranschaulicht diesen grundlegenden Unterschied zwischen den beiden Arten von Enumerationen:

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

Jedem Namen in einer Enumeration wird ein ganzzahliger Wert zugewiesen, der seiner Stelle in der Reihenfolge der Werte in der Enumeration entspricht. Standardmäßig wird dem ersten Wert 0, dem nächsten Wert 1 usw. zugewiesen. Sie können jedoch den Wert eines Enumerators explizit festlegen, wie im Folgenden dargestellt:

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

Dem Enumerator Diamonds wird der Wert 1 zugewiesen. Nachfolgende Aufzählungszeichen erhalten, wenn sie keinen expliziten Wert erhalten, den Wert des vorherigen Enumerators plus eins. Im vorherigen Beispiel würde Hearts den Wert 2, Clubs den Wert 3 usw. erhalten.

Jeder Enumerator wird als Konstante behandelt und muss einen eindeutigen Namen innerhalb des Bereichs aufweisen, in dem die enum Enums definiert sind (für nicht bereichsbezogene Enumerationen) oder innerhalb der enum selbst (für bereichsbezogene Enumerationen). Die Werte, die den Namen zugewiesen werden, müssen nicht eindeutig sein. Betrachten Sie z. B. diese Deklaration einer nicht bereichsierten Enumeration Suit:

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

Die Werte von Diamonds, Hearts, Clubs, und Spades sind 5, 6, 4 und 5, bzw. 5. Beachten Sie, dass 5 mehrmals verwendet wird; es ist zulässig, obwohl es möglicherweise nicht beabsichtigt ist. Diese Regeln sind für bereichsbezogene Enumerationen identisch.

Umwandlungsregeln

Unscoped enum constants can be implicitly converted to int, but an int is never implicitly convert to an enum value. Das folgende Beispiel zeigt, was passiert, wenn Sie versuchen, einen Wert zuzuweisen hand , der kein Suit:

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

Eine Umwandlung ist erforderlich, um einen int bereichsbezogenen oder nicht bereichsbezogenen Enumerator zu konvertieren. Sie können jedoch einen nicht bereichsierten Enumerator in einen ganzzahligen Wert ohne Umwandlung höher stufen.

int account_num = Hearts; //OK if Hearts is in an unscoped enum

Eine solche Verwendung der impliziten Konvertierungen kann zu unbeabsichtigten Nebeneffekten führen. Um Programmierfehler im Zusammenhang mit Enumerationen ohne Bereichseinschränkung zu eliminieren, sind bereichsbezogene Enumerationswerte stark typisiert. Bereichsenumeratoren müssen durch den Enumerationstypnamen (Bezeichner) qualifiziert werden und können nicht implizit konvertiert werden, wie im folgenden Beispiel gezeigt:

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

Beachten Sie, dass die Zeile hand = account_num; noch den Fehler verursacht, der bei Enumerationen ohne Bereichsbeschränkung auftritt, wie oben beschrieben. Es ist mit einer expliziten Umwandlung zulässig. Bei bereichsbezogenen Enumerationen ist der Versuch einer Konvertierung in der nächsten Anweisung, account_num = Suit::Hearts;, nicht mehr ohne eine explizite Umwandlung zulässig.

Enumerationen ohne Enumerationen

Visual Studio 2017, Version 15.3 und höher (verfügbar mit /std:c++17 und höher): Durch Definieren einer Enumeration (normal oder bereichsmäßig) mit einem explizit zugrunde liegenden Typ und ohne Enumerationen können Sie einen neuen integralen Typ einführen, der keine implizite Konvertierung in einen anderen Typ aufweist. Wenn Sie diesen Typ anstelle des integrierten zugrunde liegenden Typs verwenden, können Sie das Potenzial für subtile Fehler beseitigen, die durch versehentliche implizite Konvertierungen verursacht werden.

enum class byte : unsigned char { };

Der neue Typ ist eine exakte Kopie des zugrunde liegenden Typs und hat daher dieselbe Aufrufkonvention, was bedeutet, dass er ohne Leistungseinbußen für ABIs verwendet werden kann. Es ist keine Umwandlung erforderlich, wenn Variablen des Typs mithilfe der Direct-List-Initialisierung initialisiert werden. Das folgende Beispiel zeigt, wie Sie Enumerationen ohne Enumerationen in verschiedenen Kontexten initialisieren:

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

Siehe auch

C-Enumerationsdeklarationen
Schlüsselwörter