Share via


Výčty (C++)

Výčet je uživatelem definovaný typ, který se skládá ze sady pojmenovaných integrálních konstant, které se označují jako enumerátory.

Poznámka

Tento článek se zabývá typem jazyka enum C++ standardu ISO a s vymezeným typem (nebo typem silného typu), enum class který je zaveden v jazyce C++11. Informace o typech nebo typech public enum class v C++/CLI a C++/CX najdete v tématu enum class (C++/CLI a C++/CX).private enum class

Syntaxe

enum-name:
identifier

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

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

enum-head-name:
nested-name-specifierRozhodnoutidentifier

opaque-enum-declaration:
enum-keyattribute-specifier-seqopt optenum-head-nameenum-base;

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

Využití

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

Parametry

identifier
Název typu zadaný výčtu.

type
Základní typ výčtů; všechny enumerátory mají stejný základní typ. Může být jakýkoli celočíselný typ.

enum-list
Čárkami oddělený seznam výčtů v výčtu. Každý název enumerátoru nebo proměnné v oboru musí být jedinečný. Hodnoty však mohou být duplikovány. V neskopovaném výčtu je rozsah okolního rozsahu; v výčtu s vymezeným oborem je enum-list samotný obor. V výčtu s vymezeným oborem může být seznam prázdný, který v důsledku definuje nový celočíselný typ.

class
Pomocí tohoto klíčového slova v deklaraci zadáte výčt je vymezen a identifier musí být zadán. Klíčové slovo můžete použít struct také místo class, protože jsou v tomto kontextu sémanticky ekvivalentní.

Rozsah enumerátoru

Výčet poskytuje kontext pro popis rozsahu hodnot, které jsou reprezentovány jako pojmenované konstanty. Tyto pojmenované konstanty se také nazývají enumerátory. V původních typech jazyka C a C++ enum jsou nekvalifikované enumerátory viditelné v celém oboru, ve kterém enum je deklarován. V vymezených výčtech musí být název enumerátoru kvalifikovaný názvem enum typu. Následující příklad ukazuje tento základní rozdíl mezi dvěma druhy výčtů:

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

Každému názvu v výčtu je přiřazena integrální hodnota, která odpovídá jeho místu v pořadí hodnot v výčtu. Ve výchozím nastavení je první hodnota přiřazena 0, další hodnota je přiřazena 1 atd., ale můžete explicitně nastavit hodnotu enumerátoru, jak je znázorněno tady:

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

Výčtu Diamonds je přiřazena hodnota 1. Další enumerátory, pokud nemají explicitní hodnotu, obdrží hodnotu předchozího enumerátoru plus jeden. V předchozím příkladu Hearts by měla hodnotu 2, Clubs měla by hodnotu 3 atd.

Každý enumerátor je považován za konstantu a musí mít jedinečný název v rámci oboru, ve kterém enum je definován (pro neskopované výčty) nebo uvnitř enum samotného (pro výčty s vymezeným oborem). Hodnoty zadané názvů nemusí být jedinečné. Představte si například tuto deklaraci neskopovaného výčtu Suit:

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

Hodnoty Diamonds, , HeartsClubsa Spades jsou 5, 6, 4 a 5, v uvedeném pořadí. Všimněte si, že 5 se používá více než jednou; to je povoleno, i když to nemusí být zamýšleno. Tato pravidla jsou stejná pro výčty s vymezeným oborem.

Přetypování pravidel

Neskopované výčtové konstanty lze implicitně převést na int, ale int nikdy není implicitně konvertibilní na hodnotu výčtu. Následující příklad ukazuje, co se stane, když se pokusíte přiřadit hand hodnotu, která není Suit:

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

Přetypování je nutné k převodu int na vymezený nebo neskopovaný enumerátor. Můžete však upřednostnit neskopovaný enumerátor na celočíselnou hodnotu bez přetypování.

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

Použití implicitních převodů tímto způsobem může vést k nezamýšleným vedlejším účinkům. Chcete-li odstranit programovací chyby spojené s neskopovanými výčty, jsou oborové hodnoty výčtu silného typu. Výčty s vymezeným oborem musí být kvalifikované názvem typu výčtu (identifikátor) a nelze je implicitně převést, jak je znázorněno v následujícím příkladu:

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

Všimněte si, že řádek hand = account_num; stále způsobuje chybu, ke které dochází u neskopovaných výčtů, jak je znázorněno výše. Je povoleno explicitním přetypováním. Nicméně, s vymezenými výčty, pokus o převod v dalším příkazu , account_num = Suit::Hearts;již není povolen bez explicitního přetypování.

Výčty bez výčtů

Visual Studio 2017 verze 15.3 a novější (k dispozici s /std:c++17 a novější): Definováním výčtu (běžný nebo vymezený) s explicitním podkladovým typem a bez výčtů můžete v důsledku zavést nový integrální typ, který nemá implicitní převod na žádný jiný typ. Použitím tohoto typu místo předdefinovaného základního typu můžete eliminovat potenciál drobných chyb způsobených neúmyslnými implicitními převody.

enum class byte : unsigned char { };

Nový typ je přesná kopie základního typu, a proto má stejnou konvenci volání, což znamená, že se dá použít napříč abI bez jakéhokoli snížení výkonu. Při inicializaci proměnných typu pomocí inicializace direct-list není vyžadováno přetypování. Následující příklad ukazuje, jak inicializovat výčty bez výčtů v různých kontextech:

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

Viz také

Deklarace výčtu jazyka C
Klíčová slova