Enum против строго типизированного перечисления


84

Я новичок в программировании на C ++.

Сегодня наткнулся на новую тему: строго типизированный enum. Я немного исследовал это, но до сих пор не могу понять, зачем нам это нужно и что от этого нужно?

Например, если у нас есть:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

Зачем нужно писать:

enum class xyz{a, b, c};

Что мы здесь пытаемся сделать? Мое самое главное сомнение - как им пользоваться. Не могли бы вы привести небольшой пример, который поможет мне понять.

Ответы:


114

Хорошо, первый пример: перечисления в старом стиле не имеют собственной области видимости:

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?

Наконец, вы можете указать базовый интегральный тип перечислений C ++ 11:

enum class Foo : char { A, B, C};

Ранее базовый тип не указывался, что могло вызвать проблемы совместимости между платформами. Изменить В комментариях было указано, что вы также можете указать базовый интегральный тип перечисления «старого стиля» в C ++ 11.


Нужно ли нам объявлять / определять enum class Coloursи enum class Fruits. Потому что, когда я писал код в VS 2010. Он выдает ошибку "expects a defination or a tag name"под class.
Расми Ранджан Наяк,

Также: для "обычного" перечисления в C ++ 11, как и в C ++ 98, базовый тип по умолчанию не определен
bruziuz 01

2
Также: вы можете сделать предварительное объявление enum-s, если указан базовый тип. Для обычного перечисления в C ++ 11, C ++ 98 это не допускается. Компилятор Microsoft позволяет вам делать предварительное объявление перечисления, но это только расширение MS, оно не является стандартным (например, gcc не позволяет этого). Так что теперь это разрешено: enum ForwardDeclare: std :: uint8_t;
bruziuz 01

Можно ли иметь перечисления с областью видимости, которые также неявно преобразуются в целочисленный тип?
SS Anne

1
@SSAnne Нет, неявное преобразование, скорее, сводит на нет цель строго типизированного перечисления. Определите функцию шаблона, чтобы вместо этого явно выполнять преобразование; используйте std :: lower_type <T> :: type для извлечения типа.
David R

17

На этой странице IBM есть хорошая статья о перечислениях , она очень подробная и хорошо написана. Вот некоторые важные моменты вкратце:

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

  • Вы получаете безопасность типов, запрещая все неявные преобразования перечислений с заданной областью в другие типы.
  • Вы получаете новую область видимости, и перечисление больше не входит в охватывающую область, что позволяет избежать конфликтов имен.
  • Перечисления с областью действия дают вам возможность указать базовый тип перечисления, а для перечислений с областью действия по умолчанию используется значение int, если вы решите не указывать его.
  • Любое перечисление с фиксированным базовым типом может быть объявлено вперед.

2
Третий и четвертый пункты не относятся к перечислениям с ограниченным объемом; вы можете указать базовый тип любого перечисления.
Майк Сеймур,

1
У кого-нибудь есть ссылка на менее сломанную версию PDF? Примеры кода в нем не отображаются ни в одной из моих программ просмотра PDF, что оставляет многое для воображения.
Сара Синбэк

11

Значения 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);

5

Классы перечисления («новые перечисления», «строгие перечисления») решают три проблемы с традиционными перечислениями C ++:

  1. обычные enumsнеявно конвертируются в int, вызывая ошибки, когда кто-то не хочет, чтобы перечисление действовало как целое число.
  2. обычный enumsэкспорт их перечислителей в окружающую область видимости, вызывая конфликты имен.
  3. Базовый тип объекта не 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 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классы используются для:

  1. Картирование системы специфические коды ошибок: В <system_error>: enum class errc;
  2. Указатели безопасности: В <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Ошибки потока ввода-вывода: В <iosfwd>:enum class io_errc { stream = 1 };
  4. Обработка ошибок асинхронной связи: В <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Некоторые из них имеют операторы, например ==defined.


3

Enum Scope

Перечисления экспортируют свои перечислители в окружающую область. У этого есть два недостатка. Во-первых, это может привести к конфликту имен, если два перечислителя в разных перечислениях, объявленных в одной области, имеют одинаковое имя; во-вторых, невозможно использовать перечислитель с полным именем, включая имя перечисления.

enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}

select = ESet::a; // error
select = a;       // is ambigious
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.