Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are declarations in a switch construct legal?

Tags:

c++

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;
like image 381
L.S. Roth Avatar asked Jul 21 '20 19:07

L.S. Roth


1 Answers

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 a case label is considered a jump in this respect.

like image 157
cdhowie Avatar answered Oct 18 '22 20:10

cdhowie