Пространства имён
Варианты
Действия

Объявление перечисления

Материал из cppreference.com
< cpp‎ | language


 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Основные типы
Типы перечислений
Типы функций
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
Объявления
ссылка
указатель
массив
Block declarations
простое объявление
объявление структурных привязок (C++17)
объявление псевдонимов(C++11)
объявление псевдонимов пространств имён
using-declaration
директива using
объявление static_assert (C++11)
определение asm
объявление непрозрачного enum(C++11)
Другие объявления
определение пространств имён
объявление функции
объявление шаблона класса
объявление шаблона функции
явное инстанцирование шаблона(C++11)
явная специализация шаблона
спецификация связывания
объявление атрибута (C++11)
пустое объявление
 

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

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

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

ключевое-слово-enum атрибуты(необязательно) имя-заголовка-перечисления(необязательно) базовый-тип(необязательно)(C++11) { список-перечислителей(необязательно) } (1)
ключевое-слово-enum атрибуты(необязательно) имя-заголовка-перечисления(необязательно) базовый-тип(необязательно)
{ список-перечислителей , }
(2)
ключевое-слово-enum атрибуты(необязательно) имя-заголовка-перечисления базовый-тип(необязательно) ; (3) (начиная с C++11)
1) спецификатор enum, который появляется в последовательности-спецификаторов-объявления синтаксиса объявления: определяет тип перечисления и его перечислителей.
2) Завершающая запятая может следовать за списком-перечислителей.
3) Объявление непрозрачного перечисления: определяет тип перечисления, но не его перечислителей: после этого объявления тип является полным типом, и его размер известен. Примечание: явное объявление специализации элемента перечисления с ограниченной областью видимости шаблона класса является единственным случаем, когда спецификатор-вложенного-имени появляется перед именем-перечисления (начиная с C++14)
ключевое-слово-enum одно из enum, enum class(начиная с C++11) или enum struct(начиная с C++11)
атрибуты(C++11) необязательная последовательность из любого количества атрибутов
имя-заголовка-перечисления

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

(до C++11)

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

(начиная с C++11)
базовый-тип(C++11) двоеточие (:), за которым следует последовательность-спецификатора-типа, который является встроенным типом (если он cv-квалифицирован, квалификаторы игнорируются), который будет служить фиксированным базовым типом для этого типа перечисления
список-перечислителей список определений перечислителей, разделённых запятыми, каждый из которых является либо просто уникальным идентификатором, который становится именем перечислителя, либо уникальным идентификатором с инициализатором: идентификато = constexpr. В любом случае за идентификатором может следовать необязательная последовательность спецификаторов атрибута. (начиная с C++17)

Есть два разных вида перечислений: перечисление без области видимости (объявляется с помощью ключевого-слова enum) и перечисление с областью видимости (объявляется с помощью ключевого-слова enum class или enum struct).

Содержание

[править] Перечисление без области видимости

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

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

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;
}

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

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

Значения типа перечисления с незаданной областью видимости неявно преобразуются в целочисленные типы. Если базовый тип не фиксирован, значение может быть преобразовано в первый тип из следующего списка, способный содержать весь диапазон значений: int, unsigned int, long, unsigned long, long long или unsigned long long, расширенные целочисленные типы с более высоким приоритетом преобразования (в порядке ранжирования, предпочтительно со знаком перед беззнаковым) (начиная с C++11). Если базовый тип фиксирован, значения могут преобразовываться в продвинутый базовый тип (предпочтительнее в разрешении перегрузки), который затем может быть продвинут.

enum color { red, yellow, green = 20, blue };
color col = red;
int n = blue; // n == 21

Значения целочисленных, с плавающей запятой и перечислимых типов можно преобразовать с помощью static_cast или explicit cast, в любой тип перечисления. Если базовый тип не фиксирован, а исходное значение выходит за пределы допустимого диапазона, результат будет невыясненный (до C++17)неопределённый (начиная с C++17). (Исходное значение, преобразованное в базовый тип перечисления, если оно имеет плавающую точку, находится в диапазоне, если оно помещается в битовое поле наименьшего размера, достаточно большое, чтобы содержать все перечислители целевого перечисления.) Иначе, результат будет таким же, как результат неявного преобразования в базовый тип.

Обратите внимание, что значение после такого преобразования не обязательно совпадёт с любым из именованных перечислителей, определённых для перечисления.

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

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

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 struct|class имя { перечислитель = constexpr , перечислитель = constexpr , ... } (1)
enum struct|class имя : тип { перечислитель = constexpr , перечислитель = constexpr , ... } (2)
enum struct|class имя ; (3)
enum struct|class имя : тип ; (4)
1) объявляет тип перечисления с ограниченной областью видимости, чей базовый тип это int (ключевые слова class и struct в точности эквивалентны)
2) объявляет тип перечисления с ограниченной областью видимости, чей базовый тип это тип
3) объявление непрозрачного перечисления с ограниченной областью видимости, чей базовый тип это int
4) объявление непрозрачного перечисления с ограниченной областью видимости, чей базовый тип это тип

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

#include <iostream>
 
int main()
{
    enum class Color { red, green = 20, blue };
    Color r = Color::blue;
 
    switch(r)
    {
        case Color::red  : std::cout << "красный\n"; break;
        case Color::green: std::cout << "зелёный\n"; break;
        case Color::blue : std::cout << "синий\n";   break;
    }
    // int n = r; // ошибка: нет неявного преобразования из перечисления
                  // с областью видимости в int
    int n = static_cast<int>(r); // OK, n = 21
    std::cout << n << '\n'; // выводит 21
}
(начиная с C++11)

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

  • инициализация, это прямая инициализация списком
  • список инициализаторов содержит только один элемент
  • перечисление либо с областью видимости, либо без неё, имеет фиксированный базовый тип
  • преобразование не сужаемое

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

enum byte : unsigned char {}; // byte, это новый целочисленный тип;
                              // смотрите также std::byte (C++17)
byte b { 42 }; // OK в C++17 (прямая инициализация списком)
byte c = { 42 }; // ошибка
byte d = byte{ 42 }; // OK в C++17; то же значение, что и b
byte e { -1 }; // ошибка
 
struct A { byte b; };
A a1 = {{42}}; // ошибка (инициализация параметра конструктора копирования списком)
A a2 = {byte{42}}; // OK в C++17
 
void f(byte);
f({ 42 }); // ошибка (инициализация параметра конструктора копирования списком)
 
enum class Handle : std::uint32_t { Invalid = 0 };
Handle h { 42 }; // OK в C++17
(начиная с C++17)

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

using enum спецификатор-вложенного-имени(необязательно) имя ; (начиная с C++20)

спецификатор-вложенного-имени(необязательно) имя должно именовать не зависимый тип перечисления. Объявления перечисления находятся с помощью обычного квалифицированного или неквалифицированного поиска, в зависимости от того, присутствует ли спецификатор-вложенного-имени.

Объявление 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 конфликтуют
}
(начиная с C++20)

[править] Примечание

Макрос тест функциональности Значение Стандарт Комментарий
__cpp_enumerator_attributes 201411L (C++17) Атрибуты для перечислителей
__cpp_using_enum 201907L (C++20) using enum

[править] Пример

#include <cstdint>
#include <iostream>
 
// перечисление, которое занимает 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 << "красный"; break;
        case yellow: os << "жёлтый";  break;
        case green : os << "зелёный"; break;
        case blue  : os << "синий";   break;
        default    : os.setstate(std::ios_base::failbit);
    }
    return os;
}
 
std::ostream& operator<<(std::ostream& os, altitude al)
{
    return os << static_cast<char>(al);
}
 
namespace cxx20
{
    enum class long_long_long_name { x, y };
 
    void using_enum_demo()
    {
        std::cout << "`using enum` в C++20: __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
`using enum` в C++20: __cpp_using_enum == 201907; x

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 377 C++98 поведение было неопределённым, когда ни один целочисленный
тип не мог представить все значения перечислителя
в этом случае перечисление некорректно
CWG 518 C++98 после списка перечислителей не допускается замыкающая запятая допускается
CWG 1638 C++14 грамматика объявления непрозрачного перечисления

запрещает его использование для специализации шаблонов

спецификаторы-вложенного-имени разрешены
CWG 1766 C++98 приведение значения вне диапазона к перечислению без
фиксированного базового типа имело неопределённый результат
поведение не определено
CWG 1966 C++11 разрешение CWG проблема 1514 сделало : частью условного
выражения базовый-тип
применять разрешение только к
спецификаторам объявления элемента
CWG 2156 C++11 определения перечисления могут определять типы перечисления
с помощью using-объявлений
запрещено
CWG 2157 C++11 разрешение CWG проблема 1966 не распространялось на полные
имена перечислений
распространяется
CWG 2530 C++98 список перечислителей может содержать несколько
перечислителей с одним и тем же идентификатором
запрещено
CWG 2590 C++98 требования к размеру, представлению значений и выравниванию
перечисления не зависели от его базового типа
все они идентичны базовым типам
CWG 2621 C++20 было неясно, как имена перечислений находятся в объявлениях
using-enum
они находятся с помощью обычного поиска имени

[править] Смотрите также

(C++11)
проверяет, является ли тип типом перечисления
(шаблон класса) [править]
проверяет, является ли тип типом перечисления с ограниченной областью видимости
(шаблон класса) [править]
получает базовый целочисленный тип для данного типа перечисления
(шаблон класса) [править]
преобразует перечисление в его базовый тип
(шаблон функции) [править]
Документация C по Перечисления