Dela via


Uppräkningar (C++)

En uppräkning är en användardefinierad typ som består av en uppsättning namngivna integralkonstanter som kallas uppräknare.

Anmärkning

Den här artikeln beskriver typen ISO Standard C++ Language enum och den omfångstyp (eller starkt skrivskyddad) enum class som introduceras i C++11. Information om eller-typerna public enum classprivate enum class i C++/CLI och C++/CX finns enum class i (C++/CLI och C++/CX).

Syntax

enum-name:
identifier

enum-specifier:
enum-head { enumerator-list välja}
enum-head { enumerator-list , }

enum-head:
enum-key attribute-specifier-seq väljaenum-head-nameväljaenum-basevälja

enum-head-name:
nested-name-specifier väljaidentifier

opaque-enum-declaration:
enum-key attribute-specifier-seq väljaenum-head-nameenum-basevälja;

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-seq välja

Användning

// 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

Parameterar

identifier
Typnamnet som anges för uppräkningen.

type
Den underliggande typen av uppräknare. alla uppräknare har samma underliggande typ. Kan vara någon integrerad typ.

enum-list
Kommaavgränsad lista över uppräknarna i uppräkningen. Varje uppräknare eller variabelnamn i omfånget måste vara unikt. Värdena kan dock dupliceras. I en oscoped uppräkning är omfånget det omgivande omfånget. i en begränsad uppräkning är omfånget enum-list sig självt. I en begränsad uppräkning kan listan vara tom, vilket i själva verket definierar en ny integraltyp.

class
Genom att använda det här nyckelordet i deklarationen anger du att uppräkningen är begränsad och ett identifier måste anges. Du kan också använda nyckelordet struct i stället för class, eftersom de är semantiskt likvärdiga i den här kontexten.

Uppräkningsomfång

En uppräkning ger kontext för att beskriva ett intervall med värden som representeras som namngivna konstanter. Dessa namngivna konstanter kallas även uppräknare. I de ursprungliga C- och C++ enum -typerna visas de okvalificerade uppräknarna i hela omfånget där enum deklareras. I begränsade uppräkningar måste uppräkningsnamnet kvalificeras av enum typnamnet. I följande exempel visas den här grundläggande skillnaden mellan de två typerna av uppräkningar:

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
        { /*...*/
        }
    }
}

Varje namn i en uppräkning tilldelas ett integralvärde som motsvarar dess plats i ordningen för värdena i uppräkningen. Som standard tilldelas det första värdet 0, nästa tilldelas 1 och så vidare, men du kan uttryckligen ange värdet för en uppräknare enligt följande:

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

Uppräknaren Diamonds tilldelas värdet 1. Efterföljande uppräknare, om de inte får ett explicit värde, får värdet för den tidigare uppräknaren plus en. I föregående exempel Hearts skulle ha värdet 2, Clubs skulle ha 3 och så vidare.

Varje uppräknare behandlas som en konstant och måste ha ett unikt namn inom omfånget enum där definieras (för oscoped uppräkningar) eller inom enum sig själv (för omfångsuppräkningar). De värden som anges för namnen behöver inte vara unika. Du kan till exempel överväga den här deklarationen av en uppräkning Suitsom inte är uppsatt :

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

Värdena för Diamonds, Hearts, Clubsoch Spades är 5, 6, 4 respektive 5. Observera att 5 används mer än en gång; Det är tillåtet även om det kanske inte är avsett. Dessa regler är desamma för omfångsberäkningar.

Regler för gjutning

Unscoped-uppräkningskonstanter kan implicit konverteras till int, men en int konverteras aldrig implicit till ett uppräkningsvärde. I följande exempel visas vad som händer om du försöker tilldela hand ett värde som inte är ett Suit:

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

En gjutning krävs för att konvertera en int till en omfångsbegränsad eller oscoped uppräknare. Du kan dock höja upp en uppräkning utanscope till ett heltalsvärde utan gjutning.

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

Att använda implicita konverteringar på detta sätt kan leda till oavsiktliga biverkningar. För att eliminera programmeringsfel som är associerade med oscoped-uppräkningar skrivs begränsade uppräkningsvärden starkt. Begränsade uppräknare måste kvalificeras av namn på uppräkningstyp (identifierare) och kan inte konverteras implicit, som du ser i följande exempel:

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
    }
}

Observera att raden hand = account_num; fortfarande orsakar det fel som inträffar med oscoped uppräkningar, som du såg tidigare. Det är tillåtet med en explicit rollbesättning. Med begränsade uppräkningar tillåts dock inte längre konverteringsförsöket i nästa instruktion, account_num = Suit::Hearts;, utan explicit avbildning.

Uppräkningar utan uppräknare

Visual Studio 2017 version 15.3 och senare (Tillgänglig med /std:c++17 och senare): Genom att definiera en uppräkning (vanlig eller begränsad) med en explicit underliggande typ och inga uppräknare kan du i praktiken introducera en ny integraltyp som inte har någon implicit konvertering till någon annan typ. Genom att använda den här typen i stället för den inbyggda underliggande typen kan du eliminera risken för subtila fel som orsakas av oavsiktliga implicita konverteringar.

enum class byte : unsigned char { };

Den nya typen är en exakt kopia av den underliggande typen och har därför samma anropskonvention, vilket innebär att den kan användas mellan API:er utan prestandastraff. Ingen gjutning krävs när variabler av typen initieras med hjälp av initiering av direktlistor. I följande exempel visas hur du initierar uppräkningar utan uppräknare i olika kontexter:

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

Se även

C Uppräkningsdeklarationer
nyckelord