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