7

Почему enum class считается более безопасным в использовании, чем обычный enum?

5

Я слышал, что некоторые люди рекомендуют использовать перечисления (enum classes) в C++ из-за их типобезопасности.

Но что это на самом деле означает?

5 ответ(ов)

8

C++ имеет два типа перечислений (enum):

  1. enum class
  2. Обычные enum

Вот несколько примеров для их объявления:

enum class Color { red, green, blue }; // enum class
enum Animal { dog, cat, bird, human }; // обычный enum

В чем разница между ними?

  • enum class — имена перечислителей локальны для этого перечисления, и их значения не подвергаются неявному преобразованию в другие типы (например, в другой enum или int).
  • Обычные enum — имена перечислителей находятся в одном пространстве имен с перечислением, и их значения неявно преобразуются в целые числа и другие типы.

Пример:

enum Color { red, green, blue };                    // обычный enum 
enum Card { red_card, green_card, yellow_card };    // еще один обычный enum 
enum class Animal { dog, deer, cat, bird, human };  // enum class
enum class Mammal { kangaroo, deer, human };        // еще один enum class

void fun() {

    // примеры плохого использования обычных enum:
    Color color = Color::red;
    Card card = Card::green_card;

    int num = color;    // без проблем

    if (color == Card::red_card) // без проблем (плохо)
        cout << "плохо" << endl;

    if (card == Color::green)   // без проблем (плохо)
        cout << "плохо" << endl;

    // примеры правильного использования enum class (безопасно)
    Animal a = Animal::deer;
    Mammal m = Mammal::deer;

    int num2 = a;   // ошибка
    if (m == a)         // ошибка (хорошо)
        cout << "плохо" << endl;

    if (a == Mammal::deer) // ошибка (хорошо)
        cout << "плохо" << endl;

}

Заключение:

enum class следует предпочитать, потому что они вызывают меньше неожиданных ситуаций, которые могут привести к ошибкам.

0

Стоит отметить, что в дополнение к другим ответам, C++20 решает одну из проблем, связанных с enum class, а именно — многословность. Рассмотрим enum class под названием Color:

void foo(Color c) {
  switch (c) {
    case Color::Red: ...;
    case Color::Green: ...;
    case Color::Blue: ...;
    // и так далее
  }
}

Это выглядит многословно по сравнению с обычным enum, где имена находятся в глобальной области видимости и не требуют префикса Color::.

Однако в C++20 мы можем использовать using enum, чтобы ввести все имена из перечисления в текущую область видимости, тем самым решая эту проблему:

void foo(Color c) {
  using enum Color;
  switch (c) {
    case Red: ...;
    case Green: ...;
    case Blue: ...;
    // и так далее
  }
}

Таким образом, теперь практически нет причин избегать использования enum class.

0

Основное преимущество использования enum class по сравнению с обычными перечислениями заключается в том, что вы можете иметь одинаковые имена переменных перечислений для двух разных перечислений и при этом сможете их разрешать. Это и называется безопасностью типов (type safety), о которой упоминал автор вопроса.

Например:

enum class Color1 { red, green, blue };    // Компилируется
enum class Color2 { red, green, blue };

enum Color1 { red, green, blue };    // Не компилируется 
enum Color2 { red, green, blue };

В случае обычных перечислений компилятор не сможет различить, с каким red вы имеете дело – Color1 или Color2, как показано в следующем примере:

enum Color1 { red, green, blue };   
enum Color2 { red, green, blue };
int x = red;    // Ошибка компиляции (на какой red вы ссылаетесь?)

Таким образом, использование enum class позволяет избежать подобных конфликтов и делает код более читаемым и безопасным.

0

Перечисления используются для представления набора целочисленных значений.

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

Например:

enum class Animal { Dog, Cat, Tiger };
enum class Pets { Dog, Parrot };

В таком случае мы не можем смешивать значения Animal и Pets.

Animal a = Dog;            // Ошибка: какой DOG?
Animal a = Pets::Dog; // Pets::Dog не является Animal

Таким образом, использование enum class помогает сохранять безопасность типов и предупреждать ошибки в коде.

0

Вот перевод ваших пунктов на русский язык в стиле ответа на StackOverflow:

  1. Не производите неявное преобразование в int. Это может привести к неожиданному поведению, особенно если вы ожидаете, что значение будет оставаться в рамках типа перечисления.

  2. Можно выбрать, какой тип будет базовым. При определении перечислений в некоторых языках программирования, таких как C#, вы можете указать, на каком типе данных будет основано перечисление (например, byte, int, и т.д.).

  3. Используйте пространство имен для ENUM, чтобы избежать загрязнения. Это поможет избежать конфликта имен, особенно если вы работаете с большими проектами или библиотеками, где могут присутствовать много одинаковых имен перечислений.

  4. В сравнении с обычным классом, ENUM может быть объявлен заранее, но не имеет методов. Это упрощает использование перечислений, так как они могут быть определены до того, как будут использованы в коде, однако, в отличие от классов, они не могут содержать поведенческих методов.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь