Объявления перечислений C++
Перечисление — это пользовательский тип, состоящий из набора целочисленных констант, называемых перечислителями.
Примечание
В этом разделе рассматривается тип enum языка C++ стандарта ISO, а также ограниченный (строго типизированный) тип enum class, который впервые введен в C++11.Сведения о типах public enum class и private enum class в C++/CLI и C++/CX см. в разделе класс enum.
// unscoped enum:
enum [identifier] [: type]
{enum-list};
// scoped enum:
enum [class|struct]
[identifier] [: type]
{enum-list};
Параметры
identifier
Имя типа, присваиваемое перечислению.type
Базовый тип перечислителей; все перечислители имеют один базовый тип. Может быть любым целочисленным типом.enum-list
Разделенный запятыми список перечислителей в перечислении. Каждый перечислитель или имя переменной в области должны быть уникальными. Однако значения могут повторяться. В неограниченном перечислении областью видимости является окружающая область; в ограниченном перечислении областью видимости является сам список enum-list.class
Используя это ключевое слово в объявлении, вы указываете, что перечисление ограничено и необходимо предоставить identifier. Кроме того, ключевое слово struct используется вместо class, так как в этом контексте они семантически эквивалентны.
Заметки
Перечисление предоставляет контекст для описания диапазона значений, которые представлены в виде именованных констант и также называются перечислителями. В первоначальных типах перечислений C и C++ перечислители с неполным имеем являются видимыми внутри области видимости, в которой объявлено перечисление. В ограниченных перечислениях имя перечислителя должно уточняться именем типа перечисления. В следующем примере демонстрируется основное различие между двумя видами перечислений.
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 (для неограниченных перечислений), или в самом перечислении (для ограниченных перечислений). Значения, задаваемые имена, могут быть неуникальными. Например, для следующего объявления неограниченного перечисления 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 a 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; — больше не будет разрешена без явного приведения.