Використовуючи {} у викладі справи. Чому?


101

Який сенс у використанні {та }у 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;
}

57
Одним із застосувань може бути обмеження сфери змінних, заявлених у викладі справи.
Абхішек Бансал


1
Занадто багато відступів теж. Випадки є лише мітками в блоці оператора перемикання: вони не вводять додаткових вкладень, тому вони повинні вирівнюватися за switchключовим словом, а у другому прикладі додані висловлювання лише з відступом. Зверніть увагу на те, як у вас є незручне чотири пробіли після цього break;.
Каз

Зауважте, прийнята відповідь є лише частково вірною, оскільки коментар Джека вказує і пропускає деякі тонкощі, на які я звертаюся у своїй відповіді.
Шафік Ягмур

Як FYI: в C (навіть C11) замість C ++ ви не можете позначати декларацію; вони не належать до синтаксичної категорії statement. У C ++ ви можете (один компонент синтаксичної категорії statementє declaration statement).
Джонатан Леффлер

Відповіді:


195

{}Позначає новий блок сфери .

Розглянемо наступний дуже надуманий приклад:

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; 
    }
}

11
Насправді IMO ви отримаєте помилку компілятора, навіть якщо пропустите друге оголошення змінної x.
Бансал Абхішек

1
Хоча над використанням цього стилю та введенням великих блоків всередину оператора перемикання буде неможливо читати справи. Я вважаю за краще тримати твердження крихітними.
masoud

2
@MatthieuM. Я знаю на факт, що MS Visual Studio 2010 матиме поведінку, на яку Абхішек вказує: вона не буде компілювати будь-яку змінну декларацію всередині справи (якщо тільки ви не використовуєте фігурні дужки для позначення нового обсягу в цьому випадку). Чи відповідає це стандартам, я не знаю.
KRyan

1
@KRyan: Це не так, але це настільки безпечніша альтернатива, що я навряд чи можу звинуватити їх у здійсненні цього.
Матьє М.

6
У розділі 6.7 (3) стандарту (нумерація для чернетки 2005 року) зазначено, що ви не можете стрибати ініціалізацією, тому ви не можете мати ініціалізацію в блоці справ.
Джек Едлі

23

TL; DR

Єдиний спосіб, коли ви можете оголосити змінну за допомогою intializer або якогось нетривіального об'єкта всередині корпусу, - це ввести область блоку за допомогою {}або іншої керуючої структури, яка має власну область, як цикл, або оператор if .

Деталі Горі

Ми можемо бачити, що випадки - це просто мічені заяви, такі як мітки, що використовуються з оператором goto ( це розглянуто в проекті стандартного проекту C ++, розділ 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 ;
}

цілком дійсний ( живий приклад ). Звичайно, якщо ви хочете оголосити одну і ту ж змінну у кожному випадку, то для кожного з них потрібен власний обсяг, але це працює так само, як і поза операторами переключення , тому це не повинно бути великим сюрпризом.

Що стосується обґрунтування заборони перейти через ініціалізацію, звіт про дефекти 467, хоча висвітлює дещо іншу проблему, забезпечує розумний випадок для автоматичних змінних :

[...] автоматичні змінні, якщо явно не ініціалізовані, можуть мати невизначені ("сміття") значення, включаючи представлення пасток, [...]

Напевно, цікавіше подивитися на випадок, коли ви розширюєте сферу дії в межах переключення на кілька випадків, найвідоміші приклади цього - це, мабуть , пристрій Даффа, який виглядав би приблизно так:

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
        }
}

6

Це звичка, яка дозволяє вводити декларації змінних із винищувальним деструктором (або конфліктами сфери) у 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";
}

1

Використання дужок в комутаторі позначає новий блок сфери, як сказав Ротем.

Але це може бути для наочності, коли ви читаєте. Щоб знати, де справа зупиняється, оскільки у вас може виникнути умовна перерва.


0

Причини можуть бути:

  1. Читання, воно візуально покращує кожен випадок як розділ, що охоплює область.
  2. Оголошення різних змінних з однаковою назвою для декількох випадків комутації.
  3. Мікрооптимізація - сфера дійсно дорогої змінної, виділеної ресурсом, яку ви хочете знищити, як тільки ви покинете область справи, або навіть більш сценарій спагетті використання команди "GOTO".
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.