Freigeben über


Enumerationen (C++)

Eine Enumeration ist ein benutzerdefinierter Typ, der aus einem Satz von benannten ganzzahligen Konstanten besteht, die als Enumeratoren bezeichnet werden.

Hinweis

Dieser Artikel umfasst den enum-Typ der ISO-Standardsprache C++ und den bewerteten (oder stark typisierten) enum class-Typ, der in C++11 eingeführt wird. Weitere Informationen zu den Typen public enum class oder private enum class in C++/CLI und C++/CX, finden Sie unter enum class(C++/CLI und C++/CX).

Syntax

enum-name:
identifier

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

enum-head:
enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt

enum-head-name:
nested-name-specifieropt identifier

opaque-enum-declaration:
enum-key attribute-specifier-seq optenum-head-name enum-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:
identifier attribute-specifier-seqopt

Verbrauch

// 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 Enumeration ohne Bereichseinschränkung ist der Gültigkeitsbereich der umschließende Bereich. In einer bereichsbezogenen Enumeration ist der Bereich selbst die enum-list. In einer bereichsbezogenen Enumeration kann die Liste leer sein, die tatsächlich einen neuen integralen Typ definiert.

class
Mit diesem Schlüsselwort in der Deklaration geben Sie an, dass für die Enumeration eine Bereichsbeschränkung gilt, und ein identifier muss angegeben werden. Sie können auch das struct-Schlüsselwort anstelle von class verwenden, da beide in diesem Kontext gleichwertig 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++-enumtypen sind die nicht qualifizierten Enumeratoren überall in dem Bereich sichtbar, in dem die enum deklariert wird. In bereichsbezogenen Enumerationen muss der Enumeratorname durch den enumtypnamen 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 Enumeratoren erhalten den Wert des vorherigen Enumerators plus eins, wenn ihnen kein expliziter Wert zugeordnet wurde. 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, in dem enum (für Enumerationen ohne Bereichseinschränkung) definiert ist, oder innerhalb der enum selbst (für bereichsbezogene Enumerationen) haben. Die Werte, die für die Namen angegeben 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 };

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

Umwandlungsregeln

Enumerationskonstanten ohne Bereichseinschränkung können implizit in int konvertiert werden, aber ein int kann niemals implizit in einen Enumerationswert konvertiert werden. Das folgende Beispiel veranschaulicht, was geschieht, wenn Sie versuchen, hand einen Wert zuzuweisen, der nicht Suit ist:

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

Eine Umwandlung ist erforderlich, um int in einen bereichsbezogenen Enumerator oder einen Enumerator ohne Bereichseinschränkung zu konvertieren. Sie können jedoch einen Enumerator ohne Bereichseinschränkung auch ohne Umwandlung auf einen Ganzzahlwert heraufstufen.

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. Bereichsbezogene Enumeratoren müssen durch den Enumerationstypnamen (Bezeichner) qualifiziert werden und können nicht, wie im folgenden Beispiel gezeigt, implizit konvertiert werden:

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. Sie 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;
}

Weitere Informationen

C-Enumerationsdeklarationen
Schlüsselwörter