switch и проваливание
Когда одну переменную сравнивают с несколькими числами, цепочка else if повторяет одно и то же сравнение в каждой ветке. Инструкция switch вычисляет выражение в скобках один раз и переходит к ветке с таким же значением:
#include <iostream>
int main() {
int day = 2;
switch (day) {
case 1:
std::cout << "Monday\n";
break;
case 2:
std::cout << "Tuesday\n"; // Tuesday
break;
default:
std::cout << "another day\n";
}
}Слово case с константой и двоеточием — это метка (label): место, куда переходит выполнение, если значение выражения равно этой константе. break завершает switch. Метка default срабатывает, когда не подошла ни одна метка case. Если default нет, в этом случае не выполнится ничего.
Какие значения подходят
Выражение в switch должно быть целого типа или перечислением (enumeration): подходят int и char, а double — нет. После case пишут константу, известную при компиляции: литерал вроде 2 или 'a' либо constexpr-переменную. Две метки с одинаковым значением не скомпилируются.
switch проверяет только равенство. Условие вроде day > 5 в метку не записать — для диапазонов остаётся цепочка else if.
Проваливание
Метка не завершает код, который стоит перед ней. Без break выполнение идёт дальше через все следующие метки, включая default, пока не встретит break или конец switch. Это называют проваливанием (fall-through). Им пользуются, чтобы у нескольких меток была одна ветка:
char c = 'e';
switch (c) {
case 'a':
case 'e':
case 'o':
std::cout << "vowel\n"; // vowel
break;
default:
std::cout << "other\n";
}При c == 'e' выполнение переходит к метке case 'e':, проходит пустую метку case 'o': и печатает vowel. Буква, для которой нет метки, попадает в default.
Если перед следующей меткой уже есть код, намеренное проваливание отмечают строкой [[fallthrough]];. Она не меняет поведение программы, а сообщает компилятору и читателю, что break пропущен не по ошибке:
int level = 2;
switch (level) {
case 2:
std::cout << "admin ";
[[fallthrough]];
case 1:
std::cout << "user\n"; // admin user
break;
}При level == 1 выполнение начинается с case 1: и печатает только user.
Частая ошибка
В ветке забыли break:
int day = 1;
switch (day) {
case 1:
std::cout << "Monday\n"; // ✗
case 2:
std::cout << "Tuesday\n";
break;
}warning: this statement may fall through [-Wimplicit-fallthrough=]При day == 1 программа печатает и Monday, и Tuesday: выполнение проваливается в case 2. Об этом предупреждает GCC с флагом -Wextra, а Clang — только с флагом -Wimplicit-fallthrough. Заканчивайте каждую ветку break, а намеренное проваливание отмечайте [[fallthrough]].