ядро языка · синтаксисcore language · syntax
Скоуп-ограниченное перечисление без неявного преобразования в 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.
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.
enum Color { Red, Green };
enum Fruit { Apple, Orange };
if (Red == Apple) {} // компилируется! оба → int(0)// compiles! both → int(0)
int x = Red; // неявно, ок// implicit, ok
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
У обычного enum было два системных недостатка, которые enum class устраняет:The plain enum had two systemic flaws that enum class eliminates:
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.
Перечислители двух разных 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.
У обычного 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.
На уровне машинного кода 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;
}
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.
Для 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).
Оба варианта работают в 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.