Оператор switch
Передаёт управление одному из нескольких операторов в зависимости от значения условия.
Синтаксис
атрибуты (необязательно) switch ( оператор-инициализации (необязательно) условие ) оператор
|
|||||||||
| атрибуты | — | (начиная с C++11) любое количество атрибутов | ||
| оператор-инициализации | — | (начиная с C++17) одно из следующего
| ||
| условие | — | любое из следующего:
Значение условия должно иметь целочисленный тип или тип перечисления или классовый тип, контекстуально неявно преобразуемый в целочисленный тип или тип перечисления. Если тип (возможно, преобразованный) подлежит целочисленным продвижениям, условие преобразуется в продвинутый тип. | ||
| оператор | — | любой оператор (обычно составной оператор). Метки case: и default: разрешены в операторе, а оператор break; имеет особое значение.
|
атрибуты (необязательно) case константное-выражение : оператор
|
(1) | ||||||||
атрибуты (необязательно) default : оператор
|
(2) | ||||||||
| константное-выражение | — | постоянное выражение того же типа, что и тип условия после преобразований и целочисленного продвижения |
Объяснения
Тело оператора switch может иметь произвольное количество меток case:, если значения всех константных-выражений уникальны (после преобразований/продвижений). Может присутствовать не более одной метки default: (хотя вложенные операторы switch могут использовать свои собственные метки default: или иметь метки case:, константы которых идентичны тем, которые используются в охватывающем switch).
Если условие оценивается как значение, равное значению одного из констант-выражений, то управление передаётся оператору, помеченному этим константным-выражением.
Если условие оценивается как значение, которое не соответствует ни одной из меток case:, а метка default: присутствует, управление передаётся оператору помеченному меткой default:.
Если условие оценивается как значение, которое не соответствует ни одной из меток case:, а метка default: отсутствует, то ни один из операторов в теле switch не выполняется.
Оператор break, встречающийся в операторе, производит выход из оператора switch:
switch (1)
{
case 1:
std::cout << '1'; // печатает "1",
case 2:
std::cout << '2'; // печатает "2"
}
switch (1)
{
case 1:
std::cout << '1'; // печатает "1"
break; // и выходит из switch
case 2:
std::cout << '2';
break;
}
|
Компиляторы могут выдавать предупреждения о провале (достижение следующей метки варианта без разрыва), если только атрибут Если используется оператор-инициализации, оператор switch эквивалентен
За исключением имён, объявленных оператором инициализации (если оператор инициализации является объявлением), и имена, объявленные условием (если условие является объявлением), находятся в одной и той же области видимости, что и область видимости оператора. |
(начиная с C++17) | |||||||||||||||||||||||
Поскольку передача управления не может входить в область видимости переменной, если оператор объявления встречается внутри выражения, её область видимости должна быть определена в её собственном составном операторе:
switch (1)
{
case 1:
int x = 0; // инициалищация
std::cout << x << '\n';
break;
default:
// ошибка компиляции: переход к default:
// вход в область видимости 'x' без её инициализации
std::cout << "default\n";
break;
}
switch (1)
{
case 1:
{
int x = 0;
std::cout << x << '\n';
break;
} // область видимости 'x' заканчивается здесь
default:
std::cout << "default\n"; // нет ошибки
break;
}
Ключевые слова
Пример
В следующем коде показано несколько вариантов использования оператора switch
#include <iostream>
int main()
{
const int i = 2;
switch (i)
{
case 1:
std::cout << '1';
case 2: // выполнение начинается с этой метки case
std::cout << '2';
case 3:
std::cout << '3';
[[fallthrough]]; // атрибут С++17 для отключения предупреждения о провале
case 5:
std::cout << "45";
break; // выполнение последующих операторов прекращается
case 6:
std::cout << '6';
}
std::cout << '\n';
switch (i)
{
case 4:
std::cout << 'a';
default:
std::cout << 'd'; // нет применимых константных выражений,
// поэтому выполняется значение по умолчанию
}
std::cout << '\n';
switch (i)
{
case 4:
std::cout << 'a'; // ничего не выполняется
}
// когда перечисления используются в операторе switch, многие компиляторы выдают
// предупреждения, если один из перечислителей не обрабатывается
enum color { RED, GREEN, BLUE };
switch (RED)
{
case RED:
std::cout << "красный\n";
break;
case GREEN:
std::cout << "зелёный\n";
break;
case BLUE:
std::cout << "синий\n";
break;
}
// синтаксис оператора инициализации C++17 может быть полезен, когда нет
// неявного преобразования в целочисленный тип или тип перечисления
struct Device
{
enum State { SLEEP, READY, BAD };
auto state() const { return m_state; }
/*...*/
private:
State m_state{};
};
switch (auto dev = Device{}; dev.state())
{
case Device::SLEEP:
/*...*/
break;
case Device::READY:
/*...*/
break;
case Device::BAD:
/*...*/
break;
}
// патологические примеры
// оператор не обязательно должен быть составным оператором
switch (0)
std::cout << "это ничего не делает\n";
// метки также не требуют составного оператора
switch (int n = 1)
{
case 0:
case 1:
std::cout << n << '\n';
}
}
Вывод:
2345
d
красный
1
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 1767 | C++98 | условия типов, которые не подлежат целочисленному продвижению, не могут быть продвинуты |
условия этих типов не продвигаются |
Смотрите также
Документация C по switch
|
Внешние ссылки
| 1. | Развертывание цикла с помощью устройства Даффа |
| 2. | Устройство Даффа можно использовать для реализации сопрограмм на C/C++ |