Перечисления (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-key
attribute-specifier-seq
opt opt enum-base
enum-head-name
enum-head-name
:
nested-name-specifier
необ. identifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
необ.enum-head-name
enum-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
:
identifier
attribute-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
Разделенный запятыми список перечислителей в перечислении. Каждый перечислитель или имя переменной в области должны быть уникальными. Однако значения могут повторяться. В неуправляемом перечислении область — это окружающая область; в перечислении с заданной областью область сама по себе enum-list
. В перечислении в области список может быть пустым, который фактически определяет новый целочисленный тип.
class
Используя это ключевое слово в объявлении, вы указываете область перечисления, и identifier
необходимо указать ее. Вы также можете использовать ключевое struct
слово вместо class
семантически эквивалентного в этом контексте.
Область перечислителя
Перечисление предоставляет контекст для описания диапазона значений, представленных как именованные константы. Эти именованные константы также называются перечислителями. В исходных типах C и C++ enum
перечислители неквалифицируются во всей области, в которой объявлен.enum
В ограниченных перечислениях имя перечислителя должно быть квалифицировано по 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
определено (для неуправляемых перечислений) или внутри enum
себя (для перечисления в области). Значения, заданные именам, не должны быть уникальными. Например, рассмотрим это объявление неуправляемой перечисления Suit
:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Значения Diamonds
, Hearts
Clubs
и Spades
имеют значение 5, 6, 4 и 5 соответственно. Обратите внимание, что 5 используется более одного раза; Это разрешено, даже если оно не может быть предназначено. Такие же правила распространяются на ограниченные перечисления.
Приведение правил
Неявно преобразуемые 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
и более поздних версий): определив перечисление (обычный или ограниченный) с явным базовым типом и без перечислителей, вы можете в действительности ввести новый целочисленный тип, который не имеет неявного преобразования в любой другой тип. Используя этот тип вместо встроенного базового типа, можно исключить вероятность тонких ошибок, вызванных непреднамеренно неявными преобразованиями.
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;
}
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по