Перечисления (C++)

Перечисление — это определяемый пользователем тип, состоящий из набора именованных целочисленных констант, которые называются перечислителями.

Примечание.

В этой статье рассматриваются тип языка enum C++ стандарта ISO и тип область (или строго типизированный), enum class который представлен в C++11. Сведения о public enum class типах private enum class или типах в C++/CLI и C++/CX см. в статьяхenum class(C++/CLI и C++/CX).

Синтаксис

enum-name:
identifier

enum-specifier:
enum-head{enumerator-listнеоб.}
enum-head { enumerator-list , }

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

enum-head-name:
nested-name-specifierнеоб.identifier

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-seqнеоб.

Использование

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

Параметры

identifier
Имя типа, присваиваемое перечислению.

type
Базовый тип перечислителей; все перечислители имеют один базовый тип. Может быть любым целочисленным типом.

enum-list
Разделенный запятыми список перечислителей в перечислении. Каждый перечислитель или имя переменной в области должны быть уникальными. Однако значения могут повторяться. В перечислении un область d, область является окружающим область; в область перечислении, область сам по enum-list себе. В перечислении область может быть пустой список, который фактически определяет новый целочисленный тип.

class
Используя этот ключевое слово в объявлении, вы указываете, что перечисление область и identifier необходимо указать. Вы также можете использовать struct ключевое слово вместо classних, так как они семантически эквивалентны в этом контексте.

Перечисление область

Перечисление предоставляет контекст для описания диапазона значений, представленных как именованные константы. Эти именованные константы также называются перечислителями. В исходных типах C и C++ enum перечислители неквалифицированных отображаются на протяжении область, в которой объявлен.enum В область d перечисления имя перечислителя должно быть квалифицировано по enum имени типа. В следующем примере демонстрируется основное различие между двумя видами перечислений.

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

Каждому имени в перечислении присваивается целочисленное значение, которое соответствует определенному месту в порядке значений в перечислении. По умолчанию первому значению присваивается 0, следующему — 1 и т. д., но можно задать значение перечислителя явным образом, как показано ниже:

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

Перечислителю Diamonds присваивается значение 1. Последующие перечислители, если они не имеют явного значения, получают значение предыдущего перечислителя плюс один. В предыдущем примере Hearts имел бы значение 2, Clubs — значение 3 и т.д.

Каждый перечислитель обрабатывается как константа и должен иметь уникальное имя в область где enum определен (для не область d перечислений) или внутри enum себя (для перечислений область d). Значения, заданные именам, не должны быть уникальными. Например, рассмотрим это объявление перечисления Suitun область d:

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

Значения Diamonds, HeartsClubsи Spades имеют значение 5, 6, 4 и 5 соответственно. Обратите внимание, что 5 используется более одного раза; Это разрешено, даже если оно не может быть предназначено. Такие же правила распространяются на ограниченные перечисления.

Приведение правил

Константы перечисления un область d могут быть неявно intпреобразованы в , но int никогда неявно преобразуется в значение перечисления. В следующем примере показано, что происходит при попытке назначить hand значение, которое не Suitявляется:

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

Приведение требуется для преобразования int в область или не область управляемый перечислитель. Однако вы можете повысить уровень перечисления без область численного значения без приведения.

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

Использование подобных неявных преобразований может приводить к непредвиденным побочным эффектам. Чтобы избежать ошибок программирования, связанных с неограниченными перечислениями, значения ограниченных перечислений являются строго типизированными. Перечислители с ограниченной областью должны быть квалифицированы именем типа перечисления (идентификатором) и не могут быть неявно преобразованы, как показано в следующем примере:

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

Обратите внимание, что в строке hand = account_num; по-прежнему содержится ошибка, которая происходит при использовании неограниченных перечислений, как показано выше. Допускается явное приведение. Однако при использовании ограниченных перечислений попытка преобразования в следующем операторе — account_num = Suit::Hearts; — больше не будет разрешена без явного приведения.

Перечисления без перечислителей

Visual Studio 2017 версии 15.3 и более поздних версий (доступных и /std:c++17 более поздних версий): определяя перечисление (обычный или область d) с явным базовым типом и без перечислителей, вы можете в действительности ввести новый целочисленный тип, который не имеет неявного преобразования в любой другой тип. Используя этот тип вместо встроенного базового типа, можно исключить вероятность тонких ошибок, вызванных непреднамеренно неявными преобразованиями.

enum class byte : unsigned char { };

Новый тип представляет собой точную копию базового типа и, следовательно, имеет то же соглашение о вызовах, что означает, что его можно использовать в ABIs без каких-либо штрафов за производительность. При инициализации переменных типа не требуется приведение с помощью инициализации прямого списка. В следующем примере показано, как инициализировать перечисления без перечислителей в различных контекстах:

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

См. также

Объявления перечисления C
Ключевые слова