Difference between revisions of "cpp/language/switch"
(No need to explain the usage in “Keywords” section.) |
|||
(38 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
− | {{title|switch statement}} | + | {{title|{{tt|switch}} statement}} |
{{cpp/language/statements/navbar}} | {{cpp/language/statements/navbar}} | ||
− | Transfers control to one of | + | Transfers control to one of several statements, depending on the value of a condition. |
===Syntax=== | ===Syntax=== | ||
{{sdsc begin}} | {{sdsc begin}} | ||
− | {{sdsc| | + | {{sdsc| |
− | {{spar|attr | + | {{spar optional|attr}} {{ttb|switch}} {{ttb|(}} {{spar optional|init-statement}} {{spar|condition}} {{ttb|)}} {{spar|statement}} |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
}} | }} | ||
{{sdsc end}} | {{sdsc end}} | ||
{{par begin}} | {{par begin}} | ||
− | {{par | {{spar|attr}}{{mark c++11}} | + | {{par|{{spar|attr}}|{{mark since c++11}} any number of {{rlp|attributes}}}} |
− | + | {{par|{{spar|init-statement}}|{{mark since c++17}} any of the following: | |
− | {{par | {{spar|init-statement}}{{mark c++17}} | + | * an {{rlp|statements#Expression statements|expression statement}} (which may be a null statement {{c|;}}) |
− | * an {{rlp|statements# | + | * a {{rlpsd|declarations#Simple declaration}}, typically a declaration of a variable with initializer, but it may declare arbitrarily many variables or {{rlp|structured binding}}s |
− | * a | + | {{rrev|since=c++23| |
− | + | * an {{rlp|type alias|alias declaration}} | |
− | {{par | {{spar| | + | }} |
+ | Note that any {{spar|init-statement}} must end with a semicolon. This is why it is often described informally as an expression or a declaration followed by a semicolon.}} | ||
+ | {{par|{{spar|condition}}|a [[#Condition|condition]]}} | ||
+ | {{par|{{spar|statement}}|a statement (typically a compound statement)}} | ||
{{par end}} | {{par end}} | ||
+ | {{cpp/language/condition|usage=determine which label the control will go to}} | ||
+ | |||
+ | ====Type==== | ||
+ | {{spar|condition}} can only yield the following types: | ||
+ | * integral types | ||
+ | * enumeration types | ||
+ | * class types | ||
+ | |||
+ | If the yielded value is of a class type, it is contextually implicitly converted to an integral or enumeration type. | ||
+ | |||
+ | If the (possibly converted) type is subject to {{rlpsd|implicit conversion#Integral promotion}}s , the yielded value is converted to the promoted type. | ||
+ | |||
+ | ===Labels=== | ||
+ | Any statement within the {{c/core|switch}} statement can be labeled with one or more following labels: | ||
{{sdsc begin}} | {{sdsc begin}} | ||
− | {{sdsc|num=1| | + | {{sdsc|num=1| |
− | {{spar|attr | + | {{spar optional|attr}} {{ttb|case}} {{spar|constant-expression}} {{ttb|:}} |
}} | }} | ||
− | {{sdsc|num=2| | + | {{sdsc|num=2| |
− | {{spar|attr | + | {{spar optional|attr}} {{ttb|default:}} |
}} | }} | ||
{{sdsc end}} | {{sdsc end}} | ||
{{par begin}} | {{par begin}} | ||
− | {{par | {{spar| | + | {{par|{{spar|attr}}|{{mark since c++11}} any number of {{rlp|attributes}}}} |
+ | {{par|{{spar|constant-expression}}|a {{rlpsd|constant expression#Converted constant expression}} of the adjusted type of the {{c/core|switch}} condition}} | ||
{{par end}} | {{par end}} | ||
− | |||
− | |||
− | + | A {{c/core|case}} or {{c/core|default}} label is associated with the innermost {{c/core|switch}} statement enclosing it. | |
− | If {{ | + | If any of the following conditions is satisfied, the program is ill-formed: |
+ | * A {{c/core|switch}} statement is associated with multiple {{c/core|case}} labels whose {{spar sep|constant-expression}}s have the same value after conversions. | ||
+ | * A {{c/core|switch}} statement is associated with multiple {{c/core|default}} labels. | ||
− | + | ===Control flow transfer=== | |
+ | When the condition of a {{c/core|switch}} statement yields a (possibly converted) value: | ||
+ | * If one of the associated {{c/core|case}} label constants has the same value, control is passed to the statement labeled by the matched {{c/core|case}} label. | ||
+ | * Otherwise, if there is an associated {{c/core|default}} label, control is passed to the statement labeled by the {{c/core|default}} label. | ||
+ | * Otherwise, none of the statements in the {{c/core|switch}} statement will be executed. | ||
− | {{source|switch(1) { | + | {{c/core|case}} and {{c/core|default}} labels in themselves do not alter the flow of control. To exit from a {{c/core|switch}} statement from the middle, see {{rlp|break|{{c/core|break}} statements}}. |
− | case 1 : cout << '1'; // prints "1", | + | |
− | case 2 : cout << '2'; // then prints "2" | + | Compilers may issue warnings on fallthrough (reaching the next {{c/core|case}} or {{c/core|default}} label without a {{c/core|break}}){{rev inl|since=c++17| unless the attribute {{attr|fallthrough}} appears immediately before the {{c/core|case}} label to indicate that the fallthrough is intentional}}. |
+ | |||
+ | {{source| | ||
+ | switch (1) | ||
+ | { | ||
+ | case 1: | ||
+ | std::cout << '1'; // prints "1", | ||
+ | case 2: | ||
+ | std::cout << '2'; // then prints "2" | ||
} | } | ||
}} | }} | ||
− | {{source|switch(1) { | + | {{source| |
− | case 1 : cout << '1'; // prints "1" | + | switch (1) |
− | + | { | |
− | case 2 : cout << '2'; | + | case 1: |
− | + | std::cout << '1'; // prints "1" | |
+ | break; // and exits the switch | ||
+ | case 2: | ||
+ | std::cout << '2'; | ||
+ | break; | ||
} | } | ||
}} | }} | ||
− | {{ | + | {{rrev|since=c++17| |
− | + | {{anchor|switch statements with initializer}} | |
− | + | ==={{c/core|switch}} statements with initializer=== | |
− | + | ||
If {{spar|init-statement}} is used, the switch statement is equivalent to | If {{spar|init-statement}} is used, the switch statement is equivalent to | ||
{{sdsc begin}} | {{sdsc begin}} | ||
− | {{sdsc | | + | {{sdsc| |
{{ttb|{}}<br> | {{ttb|{}}<br> | ||
− | :{{spar| | + | :{{spar|init-statement}}<br> |
− | :{{ttb|switch}} {{ttb|(}} {{spar|condition}} {{ttb|)}} {{spar|statement}} <br> | + | :{{ttb|switch}} {{ttb|(}} {{spar|condition}} {{ttb|)}} {{spar|statement}}<br> |
− | {{ttb|} }} | + | {{ttb|}}} |
}} | }} | ||
{{sdsc end}} | {{sdsc end}} | ||
− | |||
− | |||
+ | Except that names declared by the {{spar|init-statement}} (if {{spar|init-statement}} is a declaration) and names declared by {{spar|condition}} (if {{spar|condition}} is a declaration) are in the same scope, which is also the scope of {{spar|statement}}. | ||
+ | }} | ||
+ | |||
+ | ===Notes=== | ||
Because transfer of control is {{rlp|goto|not permitted to enter the scope}} of a variable, if a declaration statement is encountered inside the {{spar|statement}}, it has to be scoped in its own compound statement: | Because transfer of control is {{rlp|goto|not permitted to enter the scope}} of a variable, if a declaration statement is encountered inside the {{spar|statement}}, it has to be scoped in its own compound statement: | ||
− | {{source|1=switch(1) { | + | {{source|1= |
− | case 1: int x = 0; // initialization | + | switch (1) |
− | + | { | |
− | + | case 1: | |
− | default: // compilation error: jump to default: would enter the scope of 'x' | + | int x = 0; // initialization |
− | + | std::cout << x << '\n'; | |
− | + | break; | |
− | + | default: | |
+ | // compilation error: jump to default: | ||
+ | // would enter the scope of 'x' without initializing it | ||
+ | std::cout << "default\n"; | ||
+ | break; | ||
} | } | ||
}} | }} | ||
− | {{source|1=switch(1) { | + | {{source|1= |
− | case 1: { | + | switch (1) |
− | + | { | |
− | + | case 1: | |
− | + | { | |
− | default: std::cout << "default\n"; // no error | + | int x = 0; |
− | + | std::cout << x << '\n'; | |
+ | break; | ||
+ | } // scope of 'x' ends here | ||
+ | default: | ||
+ | std::cout << "default\n"; // no error | ||
+ | break; | ||
} | } | ||
}} | }} | ||
===Keywords=== | ===Keywords=== | ||
− | |||
{{ltt|cpp/keyword/switch}}, | {{ltt|cpp/keyword/switch}}, | ||
{{ltt|cpp/keyword/case}}, | {{ltt|cpp/keyword/case}}, | ||
Line 107: | Line 146: | ||
===Example=== | ===Example=== | ||
{{example | {{example | ||
− | + | |The following code shows several usage cases of the {{c/core|switch}} statement: | |
− | + | |code= | |
#include <iostream> | #include <iostream> | ||
int main() | int main() | ||
{ | { | ||
− | int i = 2; | + | const int i = 2; |
− | switch (i) { | + | switch (i) |
− | case 1: std::cout << | + | { |
− | case 2: | + | case 1: |
− | case 3: std::cout << | + | std::cout << '1'; |
− | + | case 2: // execution starts at this case label | |
− | case 5: std::cout << "45"; | + | std::cout << '2'; |
− | + | case 3: | |
− | case 6: std::cout << | + | std::cout << '3'; |
+ | [[fallthrough]]; // C++17 attribute to silent the warning on fallthrough | ||
+ | case 5: | ||
+ | std::cout << "45"; | ||
+ | break; // execution of subsequent statements is terminated | ||
+ | case 6: | ||
+ | std::cout << '6'; | ||
} | } | ||
std::cout << '\n'; | std::cout << '\n'; | ||
− | switch (i) { | + | switch (i) |
− | case 4: std::cout << | + | { |
− | default: std::cout << | + | case 4: |
− | + | std::cout << 'a'; | |
+ | default: | ||
+ | std::cout << 'd'; // there are no applicable constant expressions | ||
+ | // therefore default is executed | ||
} | } | ||
− | + | ||
std::cout << '\n'; | std::cout << '\n'; | ||
− | + | ||
− | switch (i) { | + | switch (i) |
− | case 4: std::cout << | + | { |
+ | case 4: | ||
+ | std::cout << 'a'; // nothing is executed | ||
} | } | ||
− | + | ||
// when enumerations are used in a switch statement, many compilers | // when enumerations are used in a switch statement, many compilers | ||
// issue warnings if one of the enumerators is not handled | // issue warnings if one of the enumerators is not handled | ||
− | enum color {RED, GREEN, BLUE}; | + | enum color { RED, GREEN, BLUE }; |
− | switch(RED) { | + | switch (RED) |
− | case RED: | + | { |
− | case GREEN: std::cout << "green\n"; break; | + | case RED: |
− | case BLUE: | + | std::cout << "red\n"; |
+ | break; | ||
+ | case GREEN: | ||
+ | std::cout << "green\n"; | ||
+ | break; | ||
+ | case BLUE: | ||
+ | std::cout << "blue\n"; | ||
+ | break; | ||
} | } | ||
− | + | ||
// the C++17 init-statement syntax can be helpful when there is | // the C++17 init-statement syntax can be helpful when there is | ||
// no implicit conversion to integral or enumeration type | // no implicit conversion to integral or enumeration type | ||
− | + | 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; | ||
} | } | ||
− | + | ||
// pathological examples | // pathological examples | ||
− | + | ||
− | // the statement | + | // the statement does not have to be a compound statement |
− | switch(0) | + | switch (0) |
std::cout << "this does nothing\n"; | std::cout << "this does nothing\n"; | ||
− | + | ||
− | // labels | + | // labels do not require a compound statement either |
− | switch(int n = 1) | + | switch (int n = 1) |
case 0: | case 0: | ||
− | case 1: std::cout << n << '\n'; | + | case 1: |
− | + | std::cout << n << '\n'; | |
− | + | ||
} | } | ||
− | + | |output= | |
2345 | 2345 | ||
d | d | ||
Line 175: | Line 248: | ||
1 | 1 | ||
}} | }} | ||
+ | |||
+ | ===Defect reports=== | ||
+ | {{dr list begin}} | ||
+ | {{dr list item|wg=cwg|dr=1767|std=C++98|before={{spar sep|condition}}s of types that are not subject to<br>integral promotion could not be promoted|after=do not promote<br>{{spar sep|condition}}s of these types}} | ||
+ | {{dr list item|wg=cwg|dr=2629|std=C++98|before={{spar|condition}} could be a declaration of a floating-point variable|after=prohibited}} | ||
+ | {{dr list end}} | ||
===See also=== | ===See also=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc see c | c/language/switch}} | + | {{dsc see c|c/language/switch}} |
{{dsc end}} | {{dsc end}} | ||
+ | ===External links=== | ||
+ | {{elink begin}} | ||
+ | {{elink|num=1|{{enwiki|Duff's device|Loop unrolling using Duff's Device}}}} | ||
+ | {{elink|num=2|{{enwiki|Coroutine#C|Duff's device can be used to implement coroutines in C/C++}}}} | ||
+ | {{elink end}} | ||
− | + | {{langlinks|ar|de|es|fr|it|ja|pt|ru|zh}} | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + |
Latest revision as of 18:04, 11 August 2024
Transfers control to one of several statements, depending on the value of a condition.
Contents |
[edit] Syntax
attr (optional) switch ( init-statement (optional) condition ) statement
|
|||||||||
attr | - | (since C++11) any number of attributes | ||
init-statement | - | (since C++17) any of the following:
Note that any init-statement must end with a semicolon. This is why it is often described informally as an expression or a declaration followed by a semicolon. | ||
condition | - | a condition | ||
statement | - | a statement (typically a compound statement) |
[edit] Condition
A condition can either be an expression or a simple declaration.
|
(since C++26) |
- If it can be syntactically resolved as an expression, it is treated as an expression. Otherwise, it is treated as a declaration that is not a structured binding declaration(since C++26).
When control reaches condition, the condition will yield a value, which is used to determine which label the control will go to.
[edit] Expression
If condition is an expression, the value it yields is the the value of the expression.
[edit] Declaration
If condition is a simple declaration, the value it yields is the value of the decision variable (see below).
[edit] Non-structured binding declaration
The declaration has the following restrictions:
- Syntactically conforms to the following form:
|
(until C++11) |
|
(since C++11) |
- The declarator cannot specify a function or an array.
- The type specifier sequence(until C++11)declaration specifier sequence can only contain type specifiers and constexpr, and it(since C++11) cannot define a class or enumeration.
The decision varaiable of the declaration is the declared variable.
Structured binding declarationThe declaration has the following restrictions:
The decision variable of the declaration is the invented variable e introduced by the declaration. |
(since C++26) |
[edit] Type
condition can only yield the following types:
- integral types
- enumeration types
- class types
If the yielded value is of a class type, it is contextually implicitly converted to an integral or enumeration type.
If the (possibly converted) type is subject to integral promotions , the yielded value is converted to the promoted type.
[edit] Labels
Any statement within the switch statement can be labeled with one or more following labels:
attr (optional) case constant-expression :
|
(1) | ||||||||
attr (optional) default:
|
(2) | ||||||||
attr | - | (since C++11) any number of attributes |
constant-expression | - | a converted constant expression of the adjusted type of the switch condition |
A case or default label is associated with the innermost switch statement enclosing it.
If any of the following conditions is satisfied, the program is ill-formed:
- A switch statement is associated with multiple case labels whose constant-expression s have the same value after conversions.
- A switch statement is associated with multiple default labels.
[edit] Control flow transfer
When the condition of a switch statement yields a (possibly converted) value:
- If one of the associated case label constants has the same value, control is passed to the statement labeled by the matched case label.
- Otherwise, if there is an associated default label, control is passed to the statement labeled by the default label.
- Otherwise, none of the statements in the switch statement will be executed.
case and default labels in themselves do not alter the flow of control. To exit from a switch statement from the middle, see break statements.
Compilers may issue warnings on fallthrough (reaching the next case or default label without a break) unless the attribute [[fallthrough]]
appears immediately before the case label to indicate that the fallthrough is intentional(since C++17).
switch statements with initializerIf init-statement is used, the switch statement is equivalent to
Except that names declared by the init-statement (if init-statement is a declaration) and names declared by condition (if condition is a declaration) are in the same scope, which is also the scope of statement. |
(since C++17) |
[edit] Notes
Because transfer of control is not permitted to enter the scope of a variable, if a declaration statement is encountered inside the statement, it has to be scoped in its own compound statement:
[edit] Keywords
[edit] Example
The following code shows several usage cases of the switch statement:
#include <iostream> int main() { const int i = 2; switch (i) { case 1: std::cout << '1'; case 2: // execution starts at this case label std::cout << '2'; case 3: std::cout << '3'; [[fallthrough]]; // C++17 attribute to silent the warning on fallthrough case 5: std::cout << "45"; break; // execution of subsequent statements is terminated case 6: std::cout << '6'; } std::cout << '\n'; switch (i) { case 4: std::cout << 'a'; default: std::cout << 'd'; // there are no applicable constant expressions // therefore default is executed } std::cout << '\n'; switch (i) { case 4: std::cout << 'a'; // nothing is executed } // when enumerations are used in a switch statement, many compilers // issue warnings if one of the enumerators is not handled enum color { RED, GREEN, BLUE }; switch (RED) { case RED: std::cout << "red\n"; break; case GREEN: std::cout << "green\n"; break; case BLUE: std::cout << "blue\n"; break; } // the C++17 init-statement syntax can be helpful when there is // no implicit conversion to integral or enumeration type 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; } // pathological examples // the statement does not have to be a compound statement switch (0) std::cout << "this does nothing\n"; // labels do not require a compound statement either switch (int n = 1) case 0: case 1: std::cout << n << '\n'; }
Output:
2345 d red 1
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1767 | C++98 | condition s of types that are not subject to integral promotion could not be promoted |
do not promote condition s of these types |
CWG 2629 | C++98 | condition could be a declaration of a floating-point variable | prohibited |
[edit] See also
C documentation for switch
|
[edit] External links
1. | Loop unrolling using Duff's Device |
2. | Duff's device can be used to implement coroutines in C/C++ |