Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

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

Перечисление объявляется (или переобъявляется) с использованием следующего синтаксиса:

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)
1) enum-specifier , который появляется в decl-specifier-seq синтаксиса declaration : определяет тип перечисления и его перечислители.
2) После enumerator-list может следовать завершающая запятая.
3) Непрозрачное объявление перечисления: определяет тип перечисления, но не его перечислители: после этого объявления тип является полным типом и его размер известен.
enum-key -

enum

(до C++11)

один из enum , enum class , или enum struct

(начиная с C++11)
attr - (начиная с C++11) опциональная последовательность любого количества атрибутов
enum-head-name -

имя объявляемого перечисления, может быть опущено.

(до C++11)

имя объявляемого перечисления, опционально предваренное nested-name-specifier : последовательностью имен и операторов разрешения области видимости :: , заканчивающейся оператором разрешения области видимости. Может быть опущено только в неограниченных не-непрозрачных объявлениях перечислений.
nested-name-specifier может появляться только если имя перечисления присутствует и это объявление является повторным. Для непрозрачных объявлений перечислений, nested-name-specifier может появляться только перед именем перечисления в объявлениях явной специализации .
Если nested-name-specifier присутствует, то enum-specifier не может ссылаться на перечисление, просто унаследованное или введенное с помощью using объявления , и enum-specifier может появляться только в пространстве имен, охватывающем предыдущее объявление. В таких случаях nested-name-specifier не может начинаться с decltype спецификатора.

(начиная с C++11)
enum-base - (начиная с C++11) двоеточие ( : ), за которым следует type-specifier-seq , указывающий целочисленный тип (если он cv-квалифицирован, квалификации игнорируются), который будет служить фиксированным базовым типом для данного типа перечисления
enumerator-list - разделенный запятыми список определений перечислителей, каждый из которых представляет собой либо просто уникальный идентификатор , который становится именем перечислителя, либо уникальный идентификатор с константным выражением: идентификатор = constant-expression . В любом случае, за идентификатором может непосредственно следовать опциональная последовательность спецификаторов атрибутов . (начиная с C++17)

Существует два различных вида перечислений: unscoped enumeration (объявляется с помощью enum-key enum ) и scoped enumeration (объявляется с помощью enum-key enum class или enum struct ).

Содержание

Неограниченные перечисления

enum имя  (необязательно) { перечислитель = константное-выражение , перечислитель = константное-выражение , ... } (1)
enum имя  (необязательно) : тип { перечислитель = константное-выражение , перечислитель = константное-выражение , ... } (2) (начиная с C++11)
enum имя : тип ; (3) (начиная с C++11)
1) Объявляет неограниченный тип перечисления с нефиксированным базовым типом (в этом случае базовый тип является определенным реализацией целочисленным типом, который может представлять все значения перечислителей; этот тип не больше int , если только значение перечислителя не может поместиться в int или unsigned int . Если enumerator-list пуст, базовый тип такой, как если бы перечисление имело единственный перечислитель со значением 0 . Если ни один целочисленный тип не может представить все значения перечислителей, перечисление является некорректным).
2) Объявляет тип неограниченного перечисления с фиксированным базовым типом.
3) Непрозрачное объявление перечисления для неограниченного перечисления должно указывать имя и базовый тип.

Каждый enumerator становится именованной константой типа перечисления (то есть, name ), видимой в охватывающей области видимости, и может использоваться там, где требуются константы.

enum Color { red, green, blue };
Color r = red;
switch(r)
{
    case red  : std::cout << "красный\n";   break;
    case green: std::cout << "зеленый\n"; break;
    case blue : std::cout << "синий\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

Имя неограниченного перечисления может быть опущено: такое объявление только вводит перечислители в охватывающую область видимости:

enum { a, b, c = 0, d = a + 2 }; // определяет 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; // разрешено только в C++11 и позднее
int b = X::left;
int c = x.left;
int d = p->left;

В спецификаторах объявления при объявлении члена класса последовательность

enum enum-head-name :

всегда анализируется как часть объявления перечисления:

struct S
{
    enum E1 : int {};
    enum E1 : int {}; // ошибка: переопределение перечисления,
                      // НЕ анализируется как битовое поле нулевой длины типа enum E1
};
enum E2 { e1 };
void f()
{
    false ? new enum E2 : int(); // OK: 'int' НЕ анализируется как базовый тип
}
(начиная с C++11)

Имя перечисления для целей компоновки

Безымянное перечисление, которое не имеет typedef-имени для целей линковки и которое имеет перечислитель, обозначается, для целей линковки , своим базовым типом и первым перечислителем; такое перечисление считается имеющим перечислитель в качестве имени для целей линковки .

Областные перечисления

enum struct|class name { enumerator = constant-expression , enumerator = constant-expression , ... } (1)
enum struct|class name : type { enumerator = constant-expression , enumerator = constant-expression , ... } (2)
enum struct|class name ; (3)
enum struct|class name : type ; (4)
1) объявляет тип ограниченного перечисления, базовым типом которого является int (ключевые слова class и struct полностью эквивалентны)
2) объявляет тип ограниченного перечисления, базовым типом которого является type
3) непрозрачное объявление перечисления для ограниченного перечисления, базовым типом которого является int
4) непрозрачное объявление перечисления для ограниченного перечисления, базовым типом которого является type

Каждый enumerator становится именованной константой типа перечисления (то есть name ), которая содержится в области видимости перечисления и может быть доступна с использованием оператора разрешения области видимости. Не существует неявных преобразований из значений ограниченного перечислителя в целочисленные типы, хотя static_cast может быть использован для получения числового значения перечислителя.

#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; // ошибка: нет неявного преобразования из ограниченного enum в int
    int n = static_cast<int>(r); // OK, n = 21
    std::cout << n << '\n'; // выводит 21
}
(начиная с C++11)

Перечисление может быть инициализировано из целого числа без приведения типа, используя list initialization , если выполняются все следующие условия:

  • Инициализация является direct-list-initialization.
  • Список инициализации содержит только один элемент.
  • Перечисление является либо scoped, либо unscoped с фиксированным базовым типом.
  • Преобразование является non-narrowing.

Это позволяет вводить новые целочисленные типы (например, SafeInt ), которые используют те же соглашения о вызовах, что и их базовые целочисленные типы, даже на ABI, которые штрафуют передачу/возврат структур по значению.

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
(since C++17)


using enum объявление

using enum using-enum-declarator ; (since C++20)
declarator - (возможно квалифицированный) идентификатор или простой шаблонный идентификатор


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
}

Объявление using enum вводит имена перечислителей указанного перечисления, как если бы с помощью using объявления для каждого перечислителя. При нахождении в области класса, объявление using enum добавляет перечислители указанного перечисления как члены в область видимости, делая их доступными для поиска членов.

enum class fruit { orange, apple };
struct S
{
    using enum fruit; // OK: вводит orange и apple в S
};
void f()
{
    S s;
    s.orange;  // OK: ссылается на fruit::orange
    S::orange; // OK: ссылается на fruit::orange
}

Два объявления using enum , которые вводят два перечислителя с одинаковым именем, конфликтуют.

enum class fruit { orange, apple };
enum class color { red, orange };
void f()
{
    using enum fruit;    // OK
    // using enum color; // ошибка: color::orange и fruit::orange конфликтуют
}
(since C++20)

Примечания

Значения неперечислимого типа могут быть повышены или преобразованы в целочисленные типы:

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 }; // перечислители: 1, 2, 4 диапазон: 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); // неопределённое поведение с CWG 1766
access_t y = static_cast<access_t>(8);   // неопределённое поведение с CWG 1766
enum foo { a = 0, b = UINT_MAX }; // диапазон: [0, UINT_MAX]
foo x = foo(-1); // неопределённое поведение с CWG 1766,
                 // даже если базовый тип foo - unsigned int
Макроопределение для проверки возможности Значение Стандарт Возможность
__cpp_enumerator_attributes 201411L (C++17) Атрибуты для перечислителей
__cpp_using_enum 201907L (C++20) using enum

Ключевые слова

enum , struct , class , using

Пример

#include <cstdint>
#include <iostream>
// enum, занимающий 16 бит
enum smallenum: std::int16_t
{
    a,
    b,
    c
};
// color может быть red (значение 0), yellow (значение 1), green (значение 20) или blue (значение 21)
enum color
{
    red,
    yellow,
    green = 20,
    blue
};
// altitude может быть altitude::high или altitude::low
enum class altitude: char
{
    high = 'h',
    low = 'l', // завершающая запятая разрешена только после CWG 518
}; 
// константа d равна 0, константа e равна 1, константа f равна 3
enum
{
    d,
    e,
    f = e + 2
};
// типы перечислений (как с областью видимости, так и без) могут иметь перегруженные операторы
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);
}
// Перечисление с областью видимости (C++11) может быть частично эмулировано в более ранних версиях C++:
enum struct E11 { x, y }; // начиная с C++11
struct E98 { enum { x, y }; }; // OK в pre-C++11
namespace N98 { enum { x, y }; } // OK в pre-C++11
struct S98 { static const int x = 0, y = 1; }; // OK в 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 = красный
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 грамматика неполного объявления перечисления
запрещала использование для специализаций шаблонов
квалификатор вложенного имени
разрешен
CWG 1766 C++98 приведение значения вне диапазона к перечислению
без фиксированного базового типа имело неопределенный результат
поведение является неопределенным
CWG 1966 C++11 решение проблемы CWG 1514 сделало :
условного выражения частью enum-base
применять решение только к
спецификаторам объявления членов
CWG 2156 C++11 определения enum могли определять
типы перечислений с помощью using-объявлений
запрещено
CWG 2157 C++11 решение проблемы CWG 1966 не
охватывало квалифицированные имена перечислений
охвачено
CWG 2530 C++98 список перечислителей мог содержать несколько
перечислителей с одинаковым идентификатором
запрещено
CWG 2590 C++98 размер, представление значения и требования к выравниванию
перечисления не зависели от его базового типа
все они идентичны
таковым базового типа
CWG 2621 C++20 поиск имени перечисления, используемый в
using enum объявлениях, был неясен
прояснен
CWG 2877 C++20 поиск имени перечисления, используемый в
using enum объявлениях, не был только по типу
сделан только по типу

Ссылки

  • Стандарт 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 для Перечислений