Крім класів C++ підтримує перерахування. У сучасному стандарті C++ підтримуються як перерахування без видимості, які були введені в ранніх версіях C++, а також C.
- enum ColorTypes {
- Green,
- Yellow,
- Red
- };
Так і перерахування з областю видимості
- enum class ColorTypes {
- Green,
- Yellow,
- Red
- };
Відмінність перерахувань з областю видимості від перерахувань без області видимості
Відмінність перерахування з областю видимістю відрізняється від перерахунку без області видимості тим, що змінні перерахувань з областю видимості не можуть неявно перетворюватися на цілі змінні і назад. Для такого перетворення слід використовувати
static_cast
- enum class ColorTypes { Green, Yellow, Red };
- ColorTypes currentType_1 = ColorTypes::Green; // Ok
- ColorTypes currentType_2 = 2; // Error, преобразование невозможно
- int currentType_3 = ColorTypes::Red; // Error, преобразование невозможно
- int currentType_4 = Red; // Error, Red не существует в данной области видимости
- int currentType_5 = static_cast<int>(ColorTypes::Green);
Подібний контроль областей видимості дозволяє оголошувати у класі перерахування, члени яких мають однакові імена. Також надає більший контроль над кодом, хоч і накладає низку обмежень.
Як задаються значення перерахувань
За замовчуванням перерахування починається з 0, і далі відбувається інкремент членів перерахування.
- enum class ColorTypes {
- Green, // 0
- Yellow, // 1
- Red, // 2
- Black, // 3
- Blue // 4
- };
Проте є можливість ставити власні значення для перерахувань під час оголошення.
- enum class ColorTypes {
- Green = 15, // 15
- Yellow, // 16
- Red = 24, // 24
- Black = ColorTypes::Red, // 24
- Blue // 25
- };
Використання switch case для перерахувань
Перерахування як із областю видимості, так і без області видимості підтримують оператори умов та розгалуження switch/case :
- ColorTypes currentType = ColorTypes::Green;
- switch (currentType)
- {
- case ColorTypes::Green: std::cout << "Green"; break;
- case ColorTypes::Yellow: std::cout << "Yellow"; break;
- case ColorTypes::Black: std::cout << "Black"; break;
- case ColorTypes::Blue: std::cout << "Blue"; break;
- default: std::cout << "Unknown Type"; break;
- }
Якщо міркувати про контроль виконання коду і обмеження, що накладаються при використанні перерахувань з областю видимості і без області видимості, то можна змоделювати ситуацію, коли в switch/case з якоїсь причини (друкарська помилка, копіпаста, нуб, напортачили при вирішенні конфліктів при мердже гілок) потрапляють перерахування різних типів. Тоді перерахування без області видимості будуть неявно перетворені на потрібний тип компілятором і код виконається, хоча і буде помилковим, а у випадку з перерахуваннями з областю видимості компілятор повідомить про помилку і припинить складання програми.
Тобто нижче наступний код, будучи помилковим, скомпілюється:
- enum SideTypes {
- Top,
- Bottom,
- Right,
- Left
- };
- enum ColorTypes {
- Green = 8,
- Yellow,
- Red,
- Blue
- };
- int main(int argc, char *argv[])
- {
- ColorTypes currentType = ColorTypes::Green;
- switch (currentType)
- {
- case SideTypes::Top: std::cout << "Top Side"; break;
- case ColorTypes::Green: std::cout << "Green"; break;
- case ColorTypes::Yellow: std::cout << "Yellow"; break;
- case ColorTypes::Red: std::cout << "Red"; break;
- case ColorTypes::Blue: std::cout << "Blue"; break;
- default: std::cout << "Unknown Type"; break;
- }
- return 0;
- }
У кращому разі компілятор викине Warning.
- warning: case value ‘0’ not in enumerated type ‘ColorTypes’ [-Wswitch]
- case SideTypes::Top: std::cout << "Top Side"; break;
- ^
Але буває так, що програміст "краще компілятора знає і розуміє С++", і відключає попередження.
Тоді як наступний код просто не скомпілюється:
- enum class SideTypes {
- Top,
- Bottom,
- Right,
- Left
- };
- enum class ColorTypes {
- Green = 8,
- Yellow,
- Red,
- Blue
- };
- int main(int argc, char *argv[])
- {
- ColorTypes currentType = ColorTypes::Green;
- switch (currentType)
- {
- case SideTypes::Top: std::cout << "Top Side"; break;
- case ColorTypes::Green: std::cout << "Green"; break;
- case ColorTypes::Yellow: std::cout << "Yellow"; break;
- case ColorTypes::Red: std::cout << "Red"; break;
- case ColorTypes::Blue: std::cout << "Blue"; break;
- default: std::cout << "Unknown Type"; break;
- }
- return 0;
- }
Копілятор видасть помилку компіляції:
- error: could not convert ‘Top’ from ‘SideTypes’ to ‘ColorTypes’
- case SideTypes::Top: std::cout << "Top Side"; break;
- ^
Як задати конкретний цілісний тип для перерахування
Перерахування також можуть мати більш конкретизований тип, який повинен бути певним цілим типом:
- непідписаний символ;
- char;
- int;
- довгий int;
- і т.д.
Це дозволяє виділити певний обсяг пам'яті під змінні, які мають значення перерахувань. Мабуть, це може бути актуальним для embedded розробки. Залежно від цільової платформи виділятиметься певний обсяг пам'яті.
- enum class SideTypes : short int {
- Top,
- Bottom,
- Right,
- Left
- };
Ітератор для перерахування
І насамкінець зробимо ітератор для перерахувань, за допомогою яких можна використовувати range-based цикл for .
- #include <iostream>
- enum class ColorTypes
- {
- Blue,
- Red,
- Green,
- Purple,
- First=ColorTypes::Blue, // участник перечисления для первого элемента
- Last=ColorTypes::Purple // участник перечисления для последнего элемента
- };
- ColorTypes operator++(ColorTypes& x)
- {
- // std::underlying_type преобразовывает тип ColorTypes в целочисленный тип, под которым данный enum был объявлен
- return x = static_cast<ColorTypes>(std::underlying_type<ColorTypes>::type(x) + 1);
- }
- ColorTypes operator*(ColorTypes c)
- {
- return c;
- }
- ColorTypes begin(ColorTypes r)
- {
- return ColorTypes::First;
- }
- ColorTypes end(ColorTypes r)
- {
- ColorTypes l=ColorTypes::Last;
- return ++l;
- }
- int main(int argc, char *argv[])
- {
- // Используем круглые скобки для инстанцирования перечисления
- for(const auto& c : ColorTypes())
- {
- std::cout << static_cast<int>(c) << std::endl;
- }
- return 0;
- }
классная статья! Большое спасибо. Хотел бы добавить для тех, кто будет использовать это в другом классе- перед операторами и методами begin(), end() нужно поставить friend.