Использование {} в инструкции case. Зачем?
В чем смысл использования {
и }
в инструкции case
? Обычно, независимо от того, сколько строк есть в инструкции case
, все строки выполняются. Это просто правило относительно старших/более новых компиляторов или что-то позади?
int a = 0;
switch (a) {
case 0:{
std::cout << "line1\n";
std::cout << "line2\n";
break;
}
}
и
int a = 0;
switch (a) {
case 0:
std::cout << "line1\n";
std::cout << "line2\n";
break;
}
Ответы
Ответ 1
{}
обозначает новый блок scope.
Рассмотрим следующий очень надуманный пример:
switch (a)
{
case 42:
int x = GetSomeValue();
return a * x;
case 1337:
int x = GetSomeOtherValue(); //ERROR
return a * x;
}
Вы получите ошибку компилятора, поскольку x
уже определен в области.
Разделение их на их собственную под-область исключает необходимость объявлять x
вне оператора switch.
switch (a)
{
case 42: {
int x = GetSomeValue();
return a * x;
}
case 1337: {
int x = GetSomeOtherValue(); //OK
return a * x;
}
}
Ответ 2
TL; DR
Единственный способ объявить переменную с помощью intializer или некоторого нетривиального объекта внутри случая - ввести область блока с помощью {}
или другой структуры управления, которая имеет свою собственную область действия как цикл или оператор if.
Детали Gory
Мы видим, что случаи - это просто помеченные выражения, такие как метки, используемые с оператором goto (это описано в Светодиодный проект стандарта, раздел 6.1 Обозначенный оператор), и мы можем видеть из раздела 6.7
, пункт 3, что во многих случаях пропускать объявление не разрешается, в том числе с инициализацией
Можно передать в блок, но не таким образом, чтобы обходить объявления с инициализацией. Программа, которая перескакивает 87 из точки, где переменная с длительностью автоматического хранения не находится в области до точки, где она находится в области, плохо сформирована, если переменная не имеет скалярного типа, тип класса с тривиальным конструктор по умолчанию и тривиальный деструктор, cv-квалифицированная версия одного из этих типов или массив одного из предыдущих типов и объявляется без инициализатора (8.5).
и предоставляет этот пример:
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
}
Обратите внимание, что здесь есть несколько тонкостей, вам разрешено перескакивать через скалярное объявление, которое не имеет инициализации, например:
switch( n )
{
int x ;
//int x = 10 ;
case 0:
x = 0 ;
break;
case 1:
x = 1 ;
break;
default:
x = 100 ;
break ;
}
отлично (живой пример). Конечно, если вы хотите объявить одну и ту же переменную в каждом случае, то каждый из них будет нуждаться в собственной области, но он работает так же, как и вне операторов switch, поэтому это не должно быть большим сюрпризом.
Что касается обоснования отказа от предыдущей инициализации перехода, отчет о дефекте 467, хотя при рассмотрении немного другой проблемы предоставляется разумный аргумент для автоматических переменных:
[...] автоматические переменные, если они явно не инициализированы, могут иметь неопределенные значения ( "мусор" ), включая ловушки, [...]
Вероятно, более интересно взглянуть на случай, когда вы расширяете сферу действия в пределах коммутатора по нескольким случаям, наиболее известными примерами этого является, вероятно, устройство Duff, которое выглядите примерно так:
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
}
}
Ответ 3
Это привычка, которая позволяет вам вводить объявления переменных с помощью результирующего деструктора (или конфликтов областей) в предложения case
. Другой способ взглянуть на это - это писать для языка, который они хотели бы, когда все управление потоком состоит из блоков, а не последовательностей операторов.
Ответ 4
Проверьте это основное ограничение компилятора, и вы начнете задаваться вопросом, что происходит:
int c;
c=1;
switch(c)
{
case 1:
//{
int *i = new int;
*i =1;
cout<<*i;
break;
//}
default : cout<<"def";
}
Это даст вам сообщение об ошибке:
error: jump to case label [-fpermissive]
error: crosses initialization of ‘int* i’
Пока этого не будет:
int c;
c=1;
switch(c)
{
case 1:
{
int *i = new int;
*i =1;
cout<<*i;
break;
}
default : cout<<"def";
}
Ответ 5
Использование скобок в коммутаторе означает новый блок области действия, указанный Rotem.
Но это может быть и для ясности, когда вы читаете. Чтобы узнать, где останавливается случай, поскольку у вас может быть условный разрыв в нем.
Ответ 6
Причины могут быть:
- Считываемость, визуально улучшает каждый случай как раздел с областью.
- Объявление различных переменных с одинаковым именем для нескольких случаев переключения.
- Микрооптимизация - область для действительно дорогостоящей ресурсной выделенной переменной, которую вы хотите уничтожить, как только вы покинете область действия, или даже более сценарий спагетти использования команды "GOTO".