Перейти к содержимому

enum и enum class

enum — это простой способ объявить именованные константы времени компиляции.

enum расшифровывается как enumeration — «перечисление», то есть исчерпывающий список возможных вариантов. Допустим, вам нужно представить цвета светофора — как вы это сделаете? Проще всего объявить по константе на каждый возможный цвет: одну для красного, одну для желтого и одну для зеленого.

static inline const int RED = 0;
static inline const int YELLOW = 1;
static inline const int GREEN = 2;

static здесь дает константам внутреннюю линковку: каждая единица трансляции получает собственную копию, и линковщику нечего сопоставлять между файлами. inline (для переменных — начиная с C++17) разрешает одинаковые определения в нескольких единицах трансляции. Любой файл, которому нужны эти константы, может просто подключить файл, где они объявлены, не заботясь о дублировании определений.

А использовать их можно, например, так.

По сути, именно это и делает enum. Он просто позволяет объявлять такие константы с меньшими усилиями.

enum TrafficLightColors
{
RED = 0,
YELLOW = 1,
GREEN = 2,
};

Более того, указывать значения становится необязательным. Если значения опустить, компилятор проставит их автоматически, увеличивая на единицу. Так что можно написать и так:

enum TrafficLightColors
{
RED,
YELLOW,
GREEN,
};

Фактически это эквивалентно следующему:

enum TrafficLightColors
{
RED = 0,
YELLOW = RED + 1,
GREEN = YELLOW + 1,
};

А если написать:

enum TrafficLightColors
{
RED,
YELLOW = 55,
GREEN,
};

то получится эквивалент:

enum TrafficLightColors
{
RED = 0,
YELLOW = 55,
GREEN = YELLOW + 1, // 56
};

Было бы неплохо иметь возможность гарантировать, что целое число, передаваемое в некоторую функцию, — это одно из перечисленных значений. Сделать это можно с помощью struct. См. пример.

По сути, именно этого и позволяет добиться enum class, только с куда более простым синтаксисом. См. пример.

Единственное важное отличие состоит в том, что в нашей реализации на struct константы необязательно объявлять внутри пространства имен, а вот в enum class они всегда объявлены именно там. Это означает, что все константы можно втянуть в текущую область видимости через using. С enum class до C++20 так поступить было нельзя, но начиная с C++20 есть using enum TrafficLightColors;, которое импортирует перечислители в текущую область видимости.

Перечисления не ограничены типом int — для представления значений подойдет любой другой целочисленный тип. Например, можно взять байт, если максимальное значение меньше 256. Для этого базовый тип указывается после имени перечисления.

#include <stdint.h>
enum TrafficLightColor
: uint8_t // базовый тип, беззнаковое 8-битное целое
{
RED = 0,
YELLOW = 1,
GREEN = 2,
};

Кстати, в переменную типа перечисления на самом деле можно записать недопустимое значение, то есть значение, которое не совпадает ни с одной из констант. В коде такого обычно не ожидают, поэтому, встретив где-то подобное значение, разумнее всего вызвать assert(false), чтобы программа упала.

enum class TrafficLightColor
// : int // неявно int
{
RED = 0,
YELLOW = 1,
GREEN = 2,
};
int main()
{
// Создание недопустимого цвета разрешено.
TrafficLightColor color = static_cast<TrafficLightColor>(5);
return 0;
}