Для чего нужен switch
switch сравнивает одно значение со списком фиксированных вариантов и выполняет совпавшую ветку. Когда вы ловите себя на том, что пишете длинную цепочку if/else if, которая снова и снова проверяет одну и ту же переменную на разные константы, switch выражает это намерение яснее — и часто компилируется в более быструю таблицу переходов.
Он выполняет только проверки на равенство с константами времени компиляции для целочисленного значения. Он не умеет проверять диапазоны, сравнивать строки или комбинировать условия. Для всего этого оставайтесь на if/else.
Простой switch
Вы передаёте switch значение, а затем перечисляете метки case для интересующих вас значений. Каждая ветка заканчивается break:
Значение 3 совпадает с case 3:, поэтому выводится "Wednesday", и break выпрыгивает из switch. Ветка default — это универсальный случай: она выполняется, когда ни один case не совпал. Она необязательна, но включайте её, если не уверены, что обработаны все значения.
Обратите внимание, что метки case — это просто константы, за которыми следует двоеточие, а не условия. Вы пишете case 3:, но никогда case day == 3:.
break и проваливание
Это самая важная ловушка switch в C++. После того как case совпал, выполнение не останавливается на следующем case — оно продолжает идти напрямую, пока не встретит break или закрывающую скобку. Посмотрите, что происходит, когда операторы break отсутствуют:
Вы могли бы ожидать только "one". Вместо этого выводятся все четыре строки: совпадение с case 1: входит в switch в этом месте, а затем проваливается сквозь все метки ниже. Добавьте break; после каждой ветки — и вы получите единственную нужную строку. Забытый break — классический источник ошибок вида «почему мой switch выполняет слишком много кода?».
Намеренное проваливание для группировки случаев
Проваливание не всегда ошибка — это идиоматический способ заставить несколько случаев разделять одно тело. Оставьте случаи пустыми (без операторов, без break), и все они перетекут в следующий блок:
'A', 'B' и 'C' все попадают на одну строку "Pass", потому что первые два случая пусты и проваливаются в третий. Это чисто и сделано намеренно. Когда вы действительно хотите провалиться после выполнения какого-то кода, задокументируйте это комментарием — или в C++17 и более новых версиях используйте атрибут [[fallthrough]];, который сообщает компилятору «да, я так и задумал» и подавляет предупреждения о проваливании.
switch по enum
switch естественно сочетается с enum, поскольку enum — это как раз «одно из фиксированного набора значений». Компиляторы также могут предупредить вас, если вы забудете обработать один из перечислителей:
С областным enum class вы должны квалифицировать каждую метку (Direction::East). Поскольку охвачены все перечислители, default не нужен — и многие компиляторы предупредят вас, если позже вы добавите пятое направление и забудете добавить его case. Эта помощь компилятора — весомая причина предпочесть switch цепочкам if/else для enum.
Подводный камень: объявления внутри case
Нельзя объявить переменную с инициализатором в одном case и сделать её видимой во всех остальных случаях, не ограничив её область видимости. Это распространённая ошибка компиляции:
switch (x) {
case 1:
int n = 10; // ошибка: переход к case 2 обходит эту инициализацию
cout << n;
break;
case 2:
cout << "two";
break;
}
Компилятор отвергает это, потому что переход в case 2: перепрыгнул бы через инициализацию n, пока n ещё в области видимости. Решение — обернуть тело case в собственные фигурные скобки, дав переменной отдельный блок:
Каждый раз, когда case нуждается в собственной локальной переменной, заключайте его в фигурные скобки. Это также не даёт переменной «протечь» в случаи ниже.
Далее: цикл for
switch и if позволяют вашей программе выбрать, какой код выполнить один раз. Но большая часть работы — это делать одно и то же много раз: считать, перебирать список, повторять, пока не изменится условие. Цикл for — рабочая лошадка для этого, и это следующая страница.
Часто задаваемые вопросы
Когда в C++ использовать switch вместо if-else?
Используйте switch, когда сравниваете одно целочисленное значение с множеством фиксированных, постоянных вариантов — например, пунктом меню, номером дня или значением enum. Это читается яснее, чем длинная лестница if/else if, и позволяет компилятору оптимизировать ветвление. Оставайтесь на if/else, когда условия включают диапазоны (x > 10), значения с плавающей точкой, строки или несколько переменных — switch ничего из этого не умеет.
Зачем нужен break в операторе switch в C++?
Как только case совпадает, C++ продолжает выполнять следующие случаи, пока не встретит break или конец switch. Это называется проваливанием (fall-through). break его останавливает. Забытый break — классическая ошибка: вы совпадаете с case 1: и случайно выполняете ещё и код для case 2, case 3 и default.
По каким типам можно делать switch в C++?
Только по целочисленным типам или перечислениям: int, char, short, long, bool, областной/безобластной enum и всё, что приводится к одному из них. Нельзя сделать switch по double, float или std::string — для них используйте if/else. Каждая метка case должна быть константой времени компиляции.