Enum vs Сильно типизированное перечисление
Я начинаю программировать на С++.
Сегодня я сталкиваюсь с новой темой: строго набрал enum
. Я исследовал это немного, но до сих пор я не могу выяснить, зачем нам это нужно и что это значит?
Например, если мы имеем:
enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/
Зачем нам писать:
enum class xyz{a, b, c};
Что мы пытаемся сделать здесь?
Мое самое важное сомнение заключается в том, как его использовать.
Не могли бы вы привести небольшой пример, который заставит меня понять.
Ответы
Ответ 1
ОК, первый пример: перечисления старого стиля не имеют своей собственной области:
enum Animals {Bear, Cat, Chicken};
enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!
enum class Fruits { Apple, Pear, Orange };
enum class Colours { Blue, White, Orange }; // no problem!
Во-вторых, они неявно преобразуются в интегральные типы, что может привести к странному поведению:
bool b = Bear && Duck; // what?
Наконец, вы можете указать базовый интегральный тип С++ 11 перечислений:
enum class Foo : char { A, B, C};
Ранее базовый тип не указывался, что могло вызвать проблемы совместимости между платформами. Изменить. В комментариях указано, что вы также можете указать базовый интегральный тип перечня "старого стиля" в С++ 11.
Ответ 2
Там хорошая статья об enums на этой странице IBM, она очень подробно и хорошо написана. Вот несколько важных моментов:
Рекурсивные перечисления решают большинство ограничений, связанных с регулярными перечислениями: полная безопасность типов, четко определенный базовый тип, проблемы с областью и декларация.
- Вы получаете безопасность типов, запрещая все неявные преобразования перечислимых областей в другие типы.
- Вы получаете новую область, и перечисление больше не находится в закрывающей области, сохраняя себя от конфликтов имен.
- Облачные области перечисления позволяют вам указать базовый тип перечисления, а для скопированных перечислений - по умолчанию - int, если вы решите не указывать его.
- Любое перечисление с фиксированным базовым типом может быть объявлено вперед.
Ответ 3
Значения enum class
действительно имеют тип enum class
, а не underlying_type
как для C-перечислений.
enum xyz { a, b, c};
enum class xyz_c { d, f, e };
void f(xyz x)
{
}
void f_c(xyz_c x)
{
}
// OK.
f(0);
// OK for C++03 and C++11.
f(a);
// OK with C++11.
f(xyz::a);
// ERROR.
f_c(0);
// OK.
f_c(xyz_c::d);
Ответ 4
Enum Scope
Перечисления экспортируют свои счетчики в окружающую область. Это имеет два недостатка. Во-первых, это может привести к конфликтам имен, если два перечисления в разных перечислениях, объявленных в одной области, имеют одно и то же имя; во-вторых, невозможно использовать перечислитель с полным именем, включая имя перечисления.
enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}
select = ESet::a; // error
select = a; // is ambigious
Ответ 5
Классы перечисления ( "новые перечисления", "сильные перечисления" ) адресуют три проблемы с традиционными перечислениями С++:
- обычный
enums
неявно преобразовать в int
, вызывая ошибки, когда кто-то не хочет, чтобы перечисление выполнялось как целое число.
- обычный
enums
экспортирует свои счетчики в окружающий объем, вызывая конфликты имен.
- Нельзя указать базовый тип
enum
, вызывающий путаницу, проблемы с совместимостью и делает невозможным объявление вперед.
enum class
( "сильные перечисления" ) строго типизированы и ограничены:
enum Alert { green, yellow, orange, red }; // traditional enum
enum class Color { red, blue }; // scoped and strongly typed enum
// no export of enumerator names into enclosing scope
// no implicit conversion to int
enum class TrafficLight { red, yellow, green };
Alert a = 7; // error (as ever in C++)
Color c = 7; // error: no int->Color conversion
int a2 = red; // ok: Alert->int conversion
int a3 = Alert::red; // error in C++98; ok in C++11
int a4 = blue; // error: blue not in scope
int a5 = Color::blue; // error: not Color->int conversion
Color a6 = Color::blue; // ok
Как показано, традиционные перечисления работают как обычно, но теперь вы можете по желанию квалифицироваться с именем перечисления.
Новые перечисления являются "enum class", потому что они объединяют аспекты традиционных перечислений (значения имен) с аспектами классов (охваченные члены и отсутствие преобразований).
Возможность указать базовый тип позволяет упростить взаимодействие и гарантировать размеры перечислений:
enum class Color : char { red, blue }; // compact representation
enum class TrafficLight { red, yellow, green }; // by default, the underlying type is int
enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; // how big is an E?
// (whatever the old rules say;
// i.e. "implementation defined")
enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U }; // now we can be specific
Он также обеспечивает прямое объявление перечислений:
enum class Color_code : char; // (forward) declaration
void foobar(Color_code* p); // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition
Основной тип должен быть одним из целочисленных типов с подписью или без знака; по умолчанию используется int
.
В стандартной библиотеке классы enum
используются для:
- Сопоставление специфических кодов ошибок системы отображения: В
<system_error>
: enum class errc
;
- Индикаторы безопасности указателя: В
<memory>
: enum class pointer_safety { relaxed, preferred, strict };
- Ошибки потока ввода/вывода: В
<iosfwd>
: enum class io_errc { stream = 1 };
- Обработка ошибок асинхронной связи: В
<future>
: enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };
Некоторые из них имеют определенные операторы, такие как ==
.