Перечисления и switch в C++: пошаговая инструкция
За 20 минут вы напишете программу, где enum задаёт список состояний, а switch выбирает поведение для каждого. Разберём обычный enum и enum class из C++11, чтобы компилятор ловил ваши ошибки.

К концу этой инструкции у вас будет компилируемая программа: перечисление задаёт набор состояний светофора, а конструкция switch печатает для каждого свою строку. Попутно вы увидите, чем enum class из C++11 удобнее старого enum и почему компилятор начнёт ругаться на забытые ветки. На всё уйдёт минут двадцать, если вы уже умеете компилировать один файл.
Что нужно до начала
Проверьте, что окружение готово. Перечисления в стиле enum class появились в C++11, поэтому нужен компилятор с поддержкой хотя бы этого стандарта — а он есть в любом свежем GCC, Clang или MSVC.
- Компилятор: GCC 9+, Clang 10+ или MSVC (Visual Studio 2019+). Проверить версию:
g++ --versionилиclang++ --version. - Текстовый редактор или IDE — подойдёт что угодно, от VS Code до простого блокнота.
- Умение собрать один
.cpp-файл из командной строки. Флаг стандарта передаётся так:-std=c++17.
Все примеры ниже собираются командой g++ -std=c++17 main.cpp -o app (для Clang замените g++ на clang++). Стандарт C++17 берём с запасом — код будет работать и на C++11.
Шаг 1. Объявите перечисление
Перечисление — это тип, у которого фиксированный набор именованных значений. Начнём со старого стиля enum, чтобы потом было с чем сравнить.
// main.cpp, стандарт C++17
#include <iostream>
enum Light {
Red,
Yellow,
Green
};
int main() {
Light current = Red;
std::cout << "Значение current: " << current << "\n";
return 0;
}
Соберите и запустите. В консоли должно появиться: Значение current: 0. Значения нумеруются с нуля по порядку объявления, поэтому Red равно 0, Yellow — 1, Green — 2.
Обратите внимание: имена Red, Yellow, Green попадают в общую область видимости. Это и есть главная проблема старого enum — к ней вернёмся на шаге 4.
Шаг 2. Разберите значение через switch
Теперь заставим программу вести себя по-разному для каждого состояния. Замените тело main:
int main() {
Light current = Yellow;
switch (current) {
case Red:
std::cout << "Стоп\n";
break;
case Yellow:
std::cout << "Внимание\n";
break;
case Green:
std::cout << "Можно ехать\n";
break;
}
return 0;
}
Запустите. Должна появиться строка Внимание. Поменяйте current на Red — увидите Стоп. Так проверяется, что каждая ветка срабатывает правильно.
Зачем break в каждой ветке
Без break выполнение «проваливается» в следующую ветку — это поведение называется fall-through. Уберите break после case Yellow и запустите: увидите сразу Внимание и Можно ехать. Обычно это ошибка, поэтому break ставят всегда, кроме случаев, когда проваливание нужно осознанно.
Забытыйbreak— самая частая ошибка новичка вswitch. Компилятор её не считает ошибкой: с точки зрения языка это законный fall-through. Ловится только тестом или внимательным чтением.
Шаг 3. Добавьте ветку по умолчанию
Что будет, если значение не совпало ни с одной веткой? Для этого есть default:
switch (current) {
case Red:
std::cout << "Стоп\n";
break;
case Yellow:
std::cout << "Внимание\n";
break;
case Green:
std::cout << "Можно ехать\n";
break;
default:
std::cout << "Неизвестное состояние\n";
break;
}
Ветка default срабатывает, если ни один case не подошёл. У неё есть обратная сторона: если добавить в перечисление новое значение и забыть про него в switch, компилятор промолчит — сработает default. Иногда это удобно, иногда прячет забытую ветку. Ниже покажем, как получить предупреждение вместо тишины.
Шаг 4. Перейдите на enum class
Старый enum сваливает имена в общую область: если в другом месте объявить переменную Green, будет конфликт. И значения молча приводятся к int. enum class из C++11 закрывает обе проблемы.
// main.cpp, стандарт C++17
#include <iostream>
enum class Light {
Red,
Yellow,
Green
};
int main() {
Light current = Light::Green;
switch (current) {
case Light::Red:
std::cout << "Стоп\n";
break;
case Light::Yellow:
std::cout << "Внимание\n";
break;
case Light::Green:
std::cout << "Можно ехать\n";
break;
}
return 0;
}
Теперь к значениям обращаются только с префиксом типа: Light::Green. Запустите — увидите Можно ехать. Попробуйте написать current = Green без префикса: компилятор выдаст ошибку. Это и есть защита, которой не было в старом стиле.
Чем платите за enum class
Значение enum class не приводится к int автоматически. Строка std::cout << current из шага 1 больше не скомпилируется. Если число всё же нужно, приведите явно:
std::cout << static_cast<int>(current) << "\n";
Это не недостаток, а осознанное ограничение: язык требует, чтобы вы сами решили, что перечисление превращается в число.
Шаг 5. Пусть компилятор ловит забытые ветки
Уберите из switch ветку case Light::Green и не добавляйте default. Соберите с флагом предупреждений:
g++ -std=c++17 -Wall main.cpp -o app
GCC и Clang выдадут предупреждение вида enumeration value 'Green' not handled in switch. Это ровно то, что нужно: как только вы добавите в перечисление новое состояние, компилятор напомнит про каждый switch, где его забыли обработать. Поэтому в switch по enum class ветку default часто намеренно не пишут — чтобы не глушить это предупреждение.
Если не получилось
error: 'enum class' does not name a typeили похожая ошибка на самой строке enum class. Старый стандарт. Добавьте-std=c++17(или хотя бы-std=c++11) в команду сборки.error: 'Green' was not declared in this scope. Вы обратились к значениюenum classбез префикса типа. ПишитеLight::Green.error: no match for 'operator<<'при выводе переменной. Вы пытаетесь напечататьenum classнапрямую. Оберните вstatic_cast<int>(...).- Печатаются сразу две строки вместо одной. Забыт
breakв предыдущей ветке — сработал fall-through. Проверьте, что каждыйcaseзаканчиваетсяbreak. - Предупреждение о забытой ветке не появляется. Нет флага
-Wallлибо вswitchестьdefault— он гасит это предупреждение.
Сравнение двух стилей
| Свойство | enum | enum class |
|---|---|---|
| Появился в стандарте | C++98 | C++11 |
| Обращение к значению | Green | Light::Green |
| Имена в общей области видимости | Да, возможны конфликты | Нет, только через префикс |
| Автоприведение к int | Да | Нет, только через static_cast |
| Что выбрать новичку | Только для старого кода | По умолчанию |
Что дальше
Вы собрали программу с перечислением и разбором через switch, и научились ловить забытые ветки флагом -Wall. Дальше стоит посмотреть на две вещи. Первое: switch в C++ работает только с целочисленными типами и перечислениями — по строкам он не переключается, для этого используют if/else или std::map. Второе: с C++17 можно помечать осознанный fall-through атрибутом [[fallthrough]]; — почитайте о нём в справочнике cppreference, когда fall-through понадобится нарочно. Возьмите за правило собирать учебный код с -Wall с самого начала: половину ошибок компилятор находит за вас.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: cppreference: enumeration declaration, cppreference: switch statement
Частые вопросы
А если у меня старый компилятор без C++11?
Почему switch не работает со строками?
Обязательно ли ставить default?
Можно ли задать перечислению свои числовые значения?
Не сработало предупреждение о забытой ветке — что не так?
Чем enum class лучше обычного enum в реальном проекте?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.