Перечисления (enum) представляют еще один способ определения своих типов. Их отличительной особенностью является то, что они содержат набор числовых констант. Перечисление имеет следующую форму:
enum class имя_перечисления { константа_1, константа_2, ... константа_N};
После ключевых enum class идет название перечисления, и затем в фигруных скобках перечисляются через запятую константы перечисления.
Определим простейшее перечисление:
enum class Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}
В данном случае перечисление называется Day и представляет дни недели. В фигурных скобках заключены все дни недели. Фактически они представляют числовые константы.
Каждой константе сопоставляется некоторое числовое значение. По умолчанию первая константа получает в качестве значения 0, а остальные увеличиваются на единицу. Так, в примере
выше Monday будет иметь значение 0, Tuesday - 1 и так далее. Таким образом, последняя константа - Sunday будет равна 6.
После создания перечисления мы можем определить его переменную и присвоить ей одну из констант:
Day today {Day::Thursday};
// или так
//Day today = Day::Thursday;
В данном случае определяется переменная today, которая равна Day::Thursday, то есть четвертой константе перечисления Day.
Чтобы вывести значение переменной на консоль, можно использовать преобразование к типу целочисленному типу:
#include <iostream>
enum class Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
int main()
{
Day today {Day::Thursday};
std::cout << "Today: " << static_cast<int>(today) << std::endl;
}
То есть в данном случае на консоль будет выведено Today: 3, так как константа Thursday имеет значение 3.
Мы также можем управлять установкой значений в перечислении. Так, мы можем задать начальное значение для одной контанты, тогда у последуюших констант значение увеличивается на единицу:
enum class Day {Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
В данном случае Tuesday будет равно 2, а Sunday - 7.
Можно назначить каждой константе индивидуальное значение или сочетать этот подход с автоустановкой:
enum class Day {Monday = 2, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday=1};
В данном случае Saturday будет равно 7, а Sunday - 1.
Можно даже назначать двум константам одно и то же значение:
enum class Day {Monday = 1, Mon = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
Здесь константы Monday и Mon имеют одно и то же значение.
Можно присвоить константам значение уже имеющихся констант:
enum class Day {Monday = 1, Mon = Monday, Tuesday = Monday + 1, Wednesday, Thursday, Friday, Saturday, Sunday };
Стоит учитывать, что константы перечисления должны представлять целочисленные константы. Однако мы можем выбрать другой целочисленный тип, например, char:
enum class Operation: char {Add = '+', Subtract='-', Multiply='*'};
Если мы захотим вывести значения этих констант на консоль в виде символов, то необходимо преобразовать их к типу char:
#include <iostream>
enum class Operation: char {Add = '+', Subtract='-', Multiply='*'};
int main()
{
std::cout << "add: " << static_cast<char>(Operation::Add) << std::endl;
std::cout << "subtracte: " << static_cast<char>(Operation::Subtract) << std::endl;
std::cout << "multiply: " << static_cast<char>(Operation::Multiply) << std::endl;
}
Перечисления удобны, когда необходимо хранить ограниченный набор состояний и в зависимости от текущего состояния выполнять некоторые действия. Например:
#include <iostream>
enum class Operation {Add, Subtract, Multiply};
void calculate(int n1, int n2, Operation op)
{
switch (op)
{
case Operation::Add:
std::cout << n1 + n2 << std::endl;
break;
case Operation::Subtract:
std::cout << n1 - n2 << std::endl;
break;
case Operation::Multiply:
std::cout << n1 * n2 << std::endl;
break;
}
}
int main()
{
calculate(10, 6, Operation::Add); // 16
calculate(10, 6, Operation::Subtract); // 4
calculate(10, 6, Operation::Multiply); // 60
}
В данном случае все арифметические операции хранятся в перечислении Operation. В функции calculate зависимости от значения третьего параметра - применяемой операции выполняются определенные действия с двумя первыми параметрами.
При обращении к контантам перечисления по умолчанию необходимо указывать название перечисления, например, Day::Monday. Но начиная со стандарта C++20 мы можем подключить
константы перечисления в текущий контекст с помощью оператора using.
using enum Day;
И в дальнейшем использовать только имя констант:
#include <iostream>
enum class Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
using enum Day; // подключаем константы перечисления в текущую область видимости
int main()
{
Day today {Thursday}; // используем только имя константы
// или так
//Day today = Thursday;
std::cout << static_cast<int>(today) << std::endl; // 3
// выводим значение констаты Sunday
std::cout << static_cast<int>(Sunday) << std::endl; // 6
}
Также мы можем подключить только одну константу:
using Day::Monday; // подключаем только Monday
........................
Day today {Monday};
В данном случае подключаем только константу Day::Monday. Для обращения к дргуим константам по прежднему необходимо использовать имя перечисления.
Поскольку такая возможность добавлена лишь начиная со стандарта С++20, то при компиляции с g++ или clang++ добавляется соответствующий флаг - -std=c++20
Стоит отметить, что раньше в С++ использовалась другая форма перечислений, которые пришли из языка С и определяются без ключевого слова class:
#include <iostream>
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
int main()
{
Day today = Tuesday;
std::cout << today << std::endl; // 1
}
Такие перечисления еще называют unscoped (то есть не ограниченные ни какой областью видимостью). Естественно такие перечисления можно встретить в старых
программах. Однако в виду того, что они потенциально могут привести к большему количеству ошибок, то в настоящее время такая форма все меньше и меньше используется.