ядро языка · синтаксисcore language · syntax

enum class

Скоуп-ограниченное перечисление без неявного преобразования в int и без утечки имён перечислителей в окружающее пространство имён. Устраняет два независимых источника багов старого enum.A scoped enumeration with no implicit conversion to int and no leaking of enumerator names into the surrounding namespace. It eliminates two independent sources of bugs in the old-style enum.

часто используетсяcommonly used типобезопасностьtype safety

Что этоWhat it is

enum class Name { A, B, C }; (также enum struct — синоним) — перечисление, чьи значения-перечислители видны только через квалификацию Name::A и не преобразуются неявно ни в int, ни в перечислители другого enum. Обычный enum (без class) сохраняет старое поведение неограниченного скоупа и неявных преобразований — оба варианта существуют в языке параллельно.enum class Name { A, B, C }; (enum struct is a synonym) is an enumeration whose enumerator values are only visible through the qualification Name::A, and don't implicitly convert to either int or the enumerators of another enum. A plain enum (without class) keeps the old behavior of unscoped names and implicit conversions — both forms coexist in the language.

обычный enumplain enum
enum Color { Red, Green };
enum Fruit { Apple, Orange };

if (Red == Apple) {} // компилируется! оба → int(0)// compiles! both → int(0)
int x = Red;          // неявно, ок// implicit, ok
enum class
enum class Color { Red, Green };
enum class Fruit { Apple, Orange };

// Color::Red == Fruit::Apple; // ошибка компиляции// Color::Red == Fruit::Apple; // compile error
int x = Color::Red;             // ошибка — нужен static_cast// error — a static_cast is required

Зачем ввелиWhy it was introduced

У обычного enum было два системных недостатка, которые enum class устраняет:The plain enum had two systemic flaws that enum class eliminates:

01

Утечка имён в окружающий скоупName leakage into the surrounding scope

enum Color { Red, Green }; вводит Red и Green прямо в объемлющее пространство имён — конфликт имён между разными enum-ами в одном скоупе был обычным делом и требовал префиксов вроде Color_Red вручную.enum Color { Red, Green }; introduces Red and Green directly into the enclosing namespace — name clashes between different enums in the same scope were common, and required manual prefixing like Color_Red.

02

Неявное преобразование в int стирает типобезопасностьThe implicit conversion to int erases type safety

Перечислители двух разных enum сравнивались друг с другом через неявное приведение к int без единой ошибки компиляции — логическая ошибка (сравнение цвета с фруктом) оставалась незамеченной до рантайма, если вообще замечалась.Enumerators from two unrelated enums could be compared against each other via an implicit conversion to int, without a single compile error — a logic bug (comparing a color against a fruit) went unnoticed until runtime, if it was ever noticed at all.

03

Неопределённый до объявления underlying typeAn underlying type left unspecified by the declaration

У обычного enum базовый целочисленный тип выбирался компилятором из диапазона значений — предсказать размер и signedness заранее не всегда было можно; enum class позволяет указать его явно (enum class E : uint8_t), что важно для сериализации и layout.For a plain enum, the underlying integer type was chosen by the compiler based on the range of values — predicting the size and signedness ahead of time wasn't always possible. enum class lets you specify it explicitly (enum class E : uint8_t), which matters for serialization and layout.

Как это работает под капотомHow it works under the hood

На уровне машинного кода enum class — это тот же целочисленный тип, что и обычный enum: перечислитель хранится и передаётся как значение базового типа (по умолчанию — int). Разница целиком на уровне системы типов компилятора: имена перечислителей скоупятся внутри Name::, а неявные преобразования между enum class и int/другими enum отключены — нужен явный static_cast.At the machine-code level, enum class is the same integer type as a plain enum: an enumerator is stored and passed as a value of the underlying type (int by default). The difference is entirely at the compiler's type-system level: enumerator names are scoped inside Name::, and implicit conversions between an enum class and int/other enums are disabled — an explicit static_cast is required.

enum class Status : std::uint8_t { Ok, Error, Pending };  // явный underlying type// an explicit underlying type

Status s = Status::Ok;
auto raw = static_cast<std::uint8_t>(s); // явное преобразование в число// explicit conversion to a number
Status s2 = static_cast<Status>(1);      // явное преобразование из числа// explicit conversion from a number

switch (s) {
    case Status::Ok:      break;   // квалификация Status:: обязательна// the Status:: qualification is mandatory
    case Status::Error:   break;
    case Status::Pending: break;
}

ГраблиGotchas

Нет неявного преобразования в boolNo implicit conversion to bool

if (status) не компилируется для enum class, даже если один из перечислителей логически означает «ноль/ложь» — нужно явное сравнение if (status == Status::Ok) или явный static_cast.if (status) doesn't compile for an enum class, even if one of the enumerators logically means "zero/false" — an explicit comparison like if (status == Status::Ok) or an explicit static_cast is required.

Побитовые операции требуют явных операторовBitwise operations require explicit operators

Для enum class, используемого как набор флагов, a | b не компилируется по умолчанию — операторы operator|/operator& нужно перегружать вручную (частый шаблонный код в кодовых базах, использующих enum class для флагов).For an enum class used as a set of flags, a | b doesn't compile by default — the operator|/operator& overloads need to be hand-written (common boilerplate in codebases that use enum class for flags).

Старый enum и enum class совместимы по switch/casePlain enum and enum class are compatible in switch/case

Оба варианта работают в switch с одинаковым синтаксисом case — переход с обычного enum на enum class обычно требует только добавления квалификатора Name:: перед каждым перечислителем в местах использования, без изменения структуры кода.Both forms work in a switch with identical case syntax — migrating from a plain enum to an enum class usually only requires adding the Name:: qualifier before each enumerator at every use site, without changing the code's structure.