Объявление перечисления
Перечисление — это отдельный тип, значения которого ограничены диапазоном (подробности см. ниже) и который может включать несколько явно именованных констант («элементов перечисления»).
Значения констант являются значениями целочисленного типа, известного как базовый тип перечисления. Перечисление имеет тот же размер, представление значения и требования к выравниванию, что и его базовый тип. Кроме того, каждое значение перечисления имеет то же представление, что и соответствующее значение базового типа.
Перечисление объявляется (повторно) с использованием следующего синтаксиса:
enum-key attr (необязательно) enum-head-name (необязательно) enum-base (необязательно){ enumerator-list (необязательно) } | (1) | |
enum-key attr (необязательно) enum-head-name (необязательно) enum-base (необязательно){ enumerator-list , } | (2) | |
enum-key attr (необязательно) enum-head-name enum-base (необязательно) ; | (3) | (начиная с C++11) |
| enum-key | - |
|
||||
| attr | - | (начиная с C++11) необязательная последовательность из любого количества атрибутов | ||||
| enum-head-name | - |
|
||||
| enum-base | - |
(начиная с C++11) двоеточие (:), за которым следует type-specifier-seq, задающий целочисленный тип (если он имеет квалификаторы cv, они игнорируются), который будет фиксированным базовым типом данного типа перечисления |
||||
| enumerator-list | - | список определений элементов перечисления, разделённых запятыми; каждое из них представляет собой либо просто уникальный identifier, который становится именем элемента перечисления, либо уникальный идентификатор с константным выражением: identifier = constant-expression. В обоих случаях за identifier может непосредственно следовать необязательная последовательность спецификаторов атрибутов.(начиная с C++17) |
Существует два различных вида перечислений: перечисление без области видимости (объявляется с помощью enum-key enum) и перечисление с областью видимости (объявляется с помощью enum-key enum class или enum struct).
Перечисления без области видимости
enum name (необязательно) { enumerator = constant-expression , enumerator = constant-expression , ... } | (1) | |
enum name (необязательно) : type { enumerator = constant-expression , enumerator = constant-expression , ... } | (2) | (начиная с C++11) |
enum name : type ; | (3) | (начиная с C++11) |
0. Если ни один целочисленный тип не может представлять все значения элементов перечисления, программа некорректна).Каждый enumerator становится именованной константой типа перечисления (то есть name), видимой в охватывающей области видимости и применимой везде, где требуются константы.
enum Color { red, green, blue };
Color r = red;
switch(r)
{
case red : std::cout << "red\n"; break;
case green: std::cout << "green\n"; break;
case blue : std::cout << "blue\n"; break;
}
Каждому элементу перечисления соответствует значение базового типа. Когда в enumerator-list указаны =, значения элементов перечисления задаются соответствующими constant-expression. Если у первого элемента перечисления отсутствует =, соответствующее значение равно нулю. Для любого другого элемента перечисления, в определении которого отсутствует =, соответствующее значение равно значению предыдущего элемента, увеличенному на единицу.
enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12
name перечисления без области видимости можно опустить: такое объявление вводит элементы перечисления только в охватывающую область видимости:
enum { a, b, c = 0, d = a + 2 }; // defines a = 0, b = 1, c = 0, d = 2
Если перечисление без области видимости является членом класса, к его элементам можно обращаться с помощью операторов доступа к членам класса . и ->:
struct X
{
enum direction { left = 'l', right = 'r' };
};
X x;
X* p = &x;
int a = X::direction::left; // allowed only in C++11 and later
int b = X::left;
int c = x.left;
int d = p->left;
| В спецификаторах объявления объявления члена последовательность : всегда разбирается как часть объявления перечисления: struct S
{
enum E1 : int {};
enum E1 : int {}; // error: redeclaration of enumeration,
// NOT parsed as a zero-length bit-field of type enum E1
};
enum E2 { e1 };
void f()
{
false ? new enum E2 : int(); // OK: 'int' is NOT parsed as the underlying type
}
| (начиная с C++11) |
Имя перечисления для целей связывания
Для целей связывания безымянное перечисление, у которого нет имени typedef для целей связывания, но есть элемент перечисления, обозначается своим базовым типом и первым элементом; считается, что такое перечисление имеет элемент в качестве имени для целей связывания.
Перечисления с областью видимости
Каждый enumerator становится именованной константой типа перечисления (то есть name), которая входит в область видимости перечисления и доступна с помощью оператора разрешения области видимости. Неявных преобразований значений элементов перечисления с областью видимости в целочисленные типы нет, однако для получения числового значения элемента можно использовать #include <iostream>
int main()
{
enum class Color { red, green = 20, blue };
Color r = Color::blue;
switch(r)
{
case Color::red : std::cout << "red\n"; break;
case Color::green: std::cout << "green\n"; break;
case Color::blue : std::cout << "blue\n"; break;
}
// int n = r; // error: no implicit conversion from scoped enum to int
int n = static_cast<int>(r); // OK, n = 21
std::cout << n << '\n'; // prints 21
}
| (начиная с C++11) |
| Перечисление можно инициализировать целым числом без приведения типов, используя инициализацию списком, если выполнены все следующие условия:
Это позволяет вводить новые целочисленные типы (например, enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17)
byte b{42}; // OK as of C++17 (direct-list-initialization)
byte c = {42}; // error
byte d = byte{42}; // OK as of C++17; same value as b
byte e{-1}; // error
struct A { byte b; };
A a1 = {{42}}; // error (copy-list-initialization of a constructor parameter)
A a2 = {byte{42}}; // OK as of C++17
void f(byte);
f({42}); // error (copy-list-initialization of a function parameter)
enum class Handle : std::uint32_t { Invalid = 0 };
Handle h{42}; // OK as of C++17
| (начиная с C++17) |
Объявление |
using enum using-enum-declarator ; | (начиная с C++20) |
| declarator | - | (возможно, квалифицированный) идентификатор или простой идентификатор шаблона либо спецификатор типа splice(начиная с C++26), обозначающий тип перечисления |
declarator должен именовать не-зависимый тип перечисления. Объявления перечислений находятся посредством обычного поиска имён только по типам — квалифицированного или неквалифицированного, в зависимости от того, является ли declarator квалифицированным.
enum E { x };
void f()
{
int E;
using enum E; // OK
}
using F = E;
using enum F; // OK
template<class T>
using EE = T;
void g()
{
using enum EE<E>; // OK
}
Объявление usingenum вводит имена элементов указанного перечисления так, как если бы для каждого элемента было написано отдельное using-объявление. В области видимости класса объявление usingenum добавляет элементы указанного перечисления в область видимости в качестве членов, делая их доступными для поиска членов.
enum class fruit { orange, apple };
struct S
{
using enum fruit; // OK: introduces orange and apple into S
};
void f()
{
S s;
s.orange; // OK: names fruit::orange
S::orange; // OK: names fruit::orange
}
Два объявления usingenum, вводящие элементы с одинаковыми именами, конфликтуют.
enum class fruit { orange, apple };
enum class color { red, orange };
void f()
{
using enum fruit; // OK
// using enum color; // error: color::orange and fruit::orange conflict
}
Примечания
Значения перечисления без области видимости можно повысить или преобразовать к целочисленным типам:
enum color { red, yellow, green = 20, blue };
color col = red;
int n = blue; // n == 21
Значения целочисленных типов, типов с плавающей точкой и типов перечисления можно преобразовать в любой тип перечисления с помощью static_cast. Обратите внимание, что результат такого преобразования не обязательно совпадает со значением какого-либо именованного элемента данного перечисления:
enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7
access_t rwe = static_cast<access_t>(7);
assert((rwe & read) && (rwe & write) && (rwe & exec));
access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766
access_t y = static_cast<access_t>(8); // undefined behavior since CWG 1766
enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX]
foo x = foo(-1); // undefined behavior since CWG 1766,
// even if foo's underlying type is unsigned int
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_enumerator_attributes | 201411L | (C++17) | Атрибуты для элементов перечисления |
__cpp_using_enum | 201907L | (C++20) |
using enum |
Ключевые слова
Пример
#include <cstdint>
#include <iostream>
// enum that takes 16 bits
enum smallenum: std::int16_t
{
a,
b,
c
};
// color may be red (value 0), yellow (value 1), green (value 20), or blue (value 21)
enum color
{
red,
yellow,
green = 20,
blue
};
// altitude may be altitude::high or altitude::low
enum class altitude: char
{
high = 'h',
low = 'l', // trailing comma only allowed after CWG 518
};
// the constant d is 0, the constant e is 1, the constant f is 3
enum
{
d,
e,
f = e + 2
};
// enumeration types (both scoped and unscoped) can have overloaded operators
std::ostream& operator<<(std::ostream& os, color c)
{
switch(c)
{
case red : os << "red"; break;
case yellow: os << "yellow"; break;
case green : os << "green"; break;
case blue : os << "blue"; break;
default : os.setstate(std::ios_base::failbit);
}
return os;
}
std::ostream& operator<<(std::ostream& os, altitude al)
{
return os << static_cast<char>(al);
}
// The scoped enum (C++11) can be partially emulated in earlier C++ revisions:
enum struct E11 { x, y }; // since C++11
struct E98 { enum { x, y }; }; // OK in pre-C++11
namespace N98 { enum { x, y }; } // OK in pre-C++11
struct S98 { static const int x = 0, y = 1; }; // OK in pre-C++11
void emu()
{
std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4
}
namespace cxx20
{
enum class long_long_long_name { x, y };
void using_enum_demo()
{
std::cout << "C++20 `using enum`: __cpp_using_enum == ";
switch (auto rnd = []{return long_long_long_name::x;}; rnd())
{
#if defined(__cpp_using_enum)
using enum long_long_long_name;
case x: std::cout << __cpp_using_enum << "; x\n"; break;
case y: std::cout << __cpp_using_enum << "; y\n"; break;
#else
case long_long_long_name::x: std::cout << "?; x\n"; break;
case long_long_long_name::y: std::cout << "?; y\n"; break;
#endif
}
}
}
int main()
{
color col = red;
altitude a;
a = altitude::low;
std::cout << "col = " << col << '\n'
<< "a = " << a << '\n'
<< "f = " << f << '\n';
cxx20::using_enum_demo();
}
Возможный вывод:
col = red a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| CWG 377 | C++98 | поведение не было определено, если ни один целочисленный тип не мог представить все значения элементов перечисления | в этом случае перечисление является некорректным |
| CWG 518 | C++98 | после списка элементов перечисления не допускалась завершающая запятая | допускается |
| CWG 1514 | C++11 | переопределение перечисления с фиксированным базовым типом могло разбираться как битовое поле в объявлении члена класса | всегда разбирается как переопределение |
| CWG 1638 | C++11 | грамматика неполного объявления перечисления запрещала его использование для специализаций шаблонов | разрешён nested-name-specifier |
| CWG 1766 | C++98 | приведение значения, выходящего за диапазон, к перечислению без фиксированного базового типа давало неопределённый результат | поведение не определено |
| CWG 1966 | C++11 | в результате разрешения CWG 1514 :условного выражения стало частью enum-base | применять это разрешение только к спецификаторам объявления члена |
| CWG 2156 | C++11 | определения перечислений могли задавать типы перечислений с помощью using-объявлений | запрещено |
| CWG 2157 | C++11 | разрешение CWG 1966 не охватывало квалифицированные имена перечислений | охвачено |
| CWG 2530 | C++98 | список элементов перечисления мог содержать несколько элементов с одинаковым идентификатором | запрещено |
| CWG 2590 | C++98 | размер, представление значения и требования к выравниванию перечисления не зависели от его базового типа | все эти характеристики идентичны характеристикам базового типа |
| CWG 2621 | C++20 | поиск имени перечисления, используемый в объявлениях usingenum, был определён неясно | уточнено |
| CWG 2877 | C++20 | поиск имени перечисления, используемый в объявлениях usingenum, не был ограничен типами | ограничен типами |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
- 9.7.1 Объявления перечислений [dcl.enum]
- Стандарт C++20 (ISO/IEC 14882:2020):
- 9.7.1 Объявления перечислений [dcl.enum]
- Стандарт C++17 (ISO/IEC 14882:2017):
- 10.2 Объявления перечислений [dcl.enum]
- Стандарт C++14 (ISO/IEC 14882:2014):
- 7.2 Объявления перечислений [dcl.enum]
- Стандарт C++11 (ISO/IEC 14882:2011):
- 7.2 Объявления перечислений [dcl.enum]
- Стандарт C++03 (ISO/IEC 14882:2003):
- 7.2 Объявления перечислений [dcl.enum]
- Стандарт C++98 (ISO/IEC 14882:1998):
- 7.2 Объявления перечислений [dcl.enum]
См. также
|
(C++11) | проверяет, является ли тип типом перечисления (шаблон класса) |
|
(C++23) | проверяет, является ли тип типом перечисления с областью видимости (шаблон класса) |
|
(C++26) | проверяет, является ли отражённый тип типом перечисления (функция) |
|
(C++26) | проверяет, представляет ли отражение тип перечисления с областью видимости (функция) |
|
(C++26) | проверяет, представляет ли отражение элемент перечисления (функция) |
|
(C++11) | получает базовый целочисленный тип для заданного типа перечисления (шаблон класса) |
|
(C++23) | преобразует перечисление к его базовому типу (шаблон функции) |
| Документация по C о перечислениях | |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/enum