Using {} in a case statement. Why?

C++Switch Statement

C++ Problem Overview


What is the point with using { and } in a case statement? Normally, no matter how many lines are there in a case statement, all of the lines are executed. Is this just a rule regarding older/newer compilers or there is something behind that?

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}

and

int a = 0;
switch (a) {
  case 0:
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
}

C++ Solutions


Solution 1 - C++

The {} denotes a new block of scope.

Consider the following very contrived example:

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}

You will get a compiler error because x is already defined in the scope.

Separating these to their own sub-scope will eliminate the need to declare x outside the switch statement.

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

Warning:

Declare and initialize a variable inside case without {} surrounded is wrong:

#include <iostream>
using namespace std;
int main() {
	int b = 3;
	switch (b) {
	case 3:
		int a = 3; //compilation error: "initialization of 'a' skipped by 'case' label"
		return a * b;
	case 1:
		return a * b;
	}
}

Solution 2 - C++

TL;DR

The only way you can declare a variable with an intializer or some non-trivial object inside a case is to introduce a block scope using {} or other control structure that has it's own scope like a loop or if statement.

Gory details

We can see that cases are just labeled statements like the labels used with a goto statement(this is covered in the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf">C++ draft standard section 6.1 Labeled statement) and we can see from section 6.7 paragraph 3 that jumping pass a declaration is not allowed in many cases, including those with an initialization:

>It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps87 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 (8.5).

and provides this example:

void f() {
 // ...
 goto lx; // ill-formed: jump into scope of a

 ly:
  X a = 1;
 // ...
 lx:
  goto ly; // OK, jump implies destructor
          // call for a followed by construction
          // again immediately following label ly
}

Note, there are some subtleties here, you are allowed to jump past a scalar declaration that does not have an initialization, for example:

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}

is perfectly valid(http://coliru.stacked-crooked.com/a/ed1a05e9b6c612ce">live example). Of course if you want to declare the same variable in each case then they will each need their own scope but it works the same way outside of switch statements as well, so that should not be a big surprise.

As for the rationale for not allowing jump past initialization, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#467">defect report 467 although covering a slightly different issue provides a reasonable case for automatic variables:

>[...]automatic variables, if not explicitly initialized, can have indeterminate (“garbage”) values, including trap representations, [...]

It is probably more interesting to look at the case where you extend a scope within a switch over multiple cases the most famous examples of this is probably http://en.wikipedia.org/wiki/Duff%27s_device">Duff's device which would look something like this:

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            case 7:         *to = *from++;
            case 6:         *to = *from++;
            case 5:         *to = *from++;
            case 4:         *to = *from++;
            case 3:         *to = *from++;
            case 2:         *to = *from++;
            case 1:         *to = *from++;
                        } while(--n > 0);    // <- Scope end
        }
}

Solution 3 - C++

It is a habit that allows you to inject variable declarations with the resulting destructor (or scope conflicts) into case clauses. Another way of looking at it is they are writing for the language they wish they had, where all flow control consists of blocks and not sequences of statements.

Solution 4 - C++

Check this a basic compiler restriction and you will start wondering whats happening :

int c;
c=1;
	
switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}
			
    default : cout<<"def";
}

This will give you an error:

error: jump to case label [-fpermissive]
error:   crosses initialization of ‘int* i’

While this one will not:

int c;
c=1;
	
switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }
			
    default : cout<<"def";
}

Solution 5 - C++

Using brackets in switch denotes a new block of scope as said by Rotem.

But it can be as well for clarity when you read. To know where the case stops as you might have conditionnal break in it.

Solution 6 - C++

The reasons might be:

  1. Readability, it visually enhances each case as a scoped section.
  2. Declaring the a different variables with the same name for several switch cases.
  3. Micro optimizations- scope for a really expensive resource allocated variable that you want to destruct as soon as you leave the scope of the case, or even a more spaghetti scenario of the "GOTO" command usage.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmahmoodView Question on Stackoverflow
Solution 1 - C++RotemView Answer on Stackoverflow
Solution 2 - C++Shafik YaghmourView Answer on Stackoverflow
Solution 3 - C++Yakk - Adam NevraumontView Answer on Stackoverflow
Solution 4 - C++ParagView Answer on Stackoverflow
Solution 5 - C++dyesdyesView Answer on Stackoverflow
Solution 6 - C++Roman AmbinderView Answer on Stackoverflow