Skip to the content.

Enum (перечисления)


Это тип данных, где любое значение (перечислитель) определяется как символьная константа.

enum Colors
{
    // ниже перечислители - все возможные значения этого типа данных
    COLOR_RED,   // автоматом присваивается 0
    COLOR_BROWN, // 1
    COLOR_GRAY,  // 2
    COLOR_WHITE, // 3
    COLOR_PINK,  // 4
    COLOR_ORANGE,// 5
    COLOR_BLUE,  // 6
    COLOR_PURPLE,// 7
    // последняя запятая разрешена начиная с С++11
};

Colors background_color = COLOR_RED;
Colors font_color = COLOR_PINK;

std::cout << font_color; // 4

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

Стоит обратить внимание на то, что разные enum не могут содержать перечислители с одинаковыми именами (поскольку перечислители вместе с enum находяться в одном пространстве имён):

enum Colors
{
	YELLOW,
	BLACK,
	PINK
};
 
enum Feelings
{
	SAD,
	ANGRY,
	BLACK // error
};

А потому нередко можно встретить названия перечислителей с названием enum в качестве префикса: COLORS_BLACK.

Так же перечислителям можно самому определять значения (при этом не определенные будут иметь значение больше на единицу чем прошлый):

enum Animals
{
    ANIMAL_PIG = -4,
    ANIMAL_LION, // автоматически присваивается -3
    ANIMAL_CAT, // -2
    ANIMAL_HORSE = 6,
    ANIMAL_ZEBRA = 6, // значения могут повторяться
    ANIMAL_COW // 7
};

Компилятор не будет неявно конвертировать целочисленное значение в значение перечислителя:

Colors color = 7; // error

Однако это можно сделать при помощи static_cast:

Colors color = static_cast<Colors>(7); // лучше так не делать

Однако у стандратного enum есть свои минусы:

enum Colors
{
    COLORS_RED,
    COLORS_GREEN,
    COLORS_BLUE
};

enum Cars
{
    CARS_RED,
    CARS_GREEN,
    CARS_BLUE
};

int main()
{
    // создают перечислители в глобальной области видимости (из-за чего нельзя давать перечислителям разных enum одинаковые имена + загрязнение global scope):
    // можно преобразовать в int:
    int x = COLORS_RED;
    int y = CARS_RED;

    // более того компилятор их и так неявно преобразует в __int__ при сравнении:
    if (x == y) std::cout << "true\n"; // 0 == 0
    if (x == CARS_RED) std::cout << "true\n";
    if (y == COLORS_RED) std::cout << "true\n";
    if (CARS_RED == COLORS_RED) std::cout << "true\n"; // WARNING ONLY

    return 0;
}

Для решения этих проблем используют enum class (перечисления с областью видимости):

// теперь разные enum могут содержать одинаковые названия перечислителей
enum class Colors
{
    RED,
    GREEN,
    BLUE
};

enum class Cars
{
    RED,
    GREEN,
    BLUE
};

int main()
{
    // вместо префикса теперь название пространства имен
    int x = Colors::RED; // ERROR
    Colors y = Colors::RED; // OK
    
    if (Colors::RED == Cars::RED) {} // ERROR, разные типы
    if (y == Colors::RED) {} // OK

    return 0;
}