I have found multiple questions regarding the subject of defining variables inside a switch
construct, but I have not yet found a clear answer to this question.
Chapter 5.3.2 of the book C++ Primer says the following:
As we’ve seen, execution in a switch can jump across case labels. When execution jumps to a particular case, any code that occurred inside the switch before that label is ignored.
Considering this information, I do not understand why the example below is legal. If control jumps to the false
case, it should ignore the true
case. This means that, assigning to i
should be illegal, because it was never declared. Why is this construct legal?
case true:
int i;
break;
case false:
i = 42;
break;
Declaration is a compile-time thing, and what happens at runtime is irrelevant to that fact. i
is visible at any point within the same or child scope after its declaration.
There is nothing that causes a scope change between the two cases, so i
remains visible in the false
case regardless of whether the true
case executed.
This is why you may see anonymous blocks ({ }
) used to artificially constrain scope in switch cases. It's to prevent exactly this potential issue (though in this case it's not an issue).
case true: {
int i;
break;
} // Closing this block causes the end of i's lifetime.
case false: {
i = 42; // Compile-time error; i is no longer in scope.
break;
}
Note that your code becomes illegal just by initializing i
. Jumps cannot cross over initialization in either direction.
case true:
int i = 0;
break;
case false: // error: jump to case label crosses initialization
i = 42;
break;
Also, any variable of a type that is not trivial cannot have a lifetime spanning cases even if it is not explicitly initialized.
case true:
std::string i;
break;
case false: // error: jump to case label crosses initialization
i = "42";
break;
The fix in this case is to use anonymous blocks to constrain the scope the declaration of i
to not span multiple cases.
The relevant standardese:
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps* from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer.
-- C++14 (N4296) [stmt.dcl.3]
The footnote (*) regarding jumps:
The transfer from the condition of a
switch
statement to acase
label is considered a jump in this respect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With