Чому змінні не можуть бути оголошені в операторі перемикача?


944

Я завжди цікавився цим питанням - чому ви не можете оголосити змінні після мітки справи в операторі перемикача? У C ++ ви можете декларувати змінні в будь-якому місці (і оголосити їх близько до першого використання, очевидно, це добре), але наступне все одно не буде працювати:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

Вищезазначене дає мені таку помилку (MSC):

ініціалізація 'newVal' пропускається міткою 'case'

Це, мабуть, є обмеженням і для інших мов. Чому це така проблема?


10
Для пояснення на основі граматики C BNF см stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c / ...
Johne

Ось по-справжньому добре читайте про заяви переключення та мітки (ABC :) загалом.
Етереалоне

4
Я б сказав: "Чому змінні не можуть бути ініціалізовані в операторі перемикання, а не оголошені". Оскільки лише декларування змінної дає мені лише попередження в MSVC.
ZoomIn

Відповіді:


1141

Caseзаяви є лише мітками . Це означає, що компілятор буде інтерпретувати це як стрибок безпосередньо до мітки. У C ++ проблема тут є однією із сфери застосування. Ваші фігурні дужки визначають область, як усе, що знаходиться всередині switchвиписки. Це означає, що вам залишається область, де далі буде здійснено стрибок у код, пропускаючи ініціалізацію.

Правильний спосіб вирішити це - визначити область, специфічну для цього caseоператора, та визначити змінну в ньому:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

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

10
Я погоджуюся з Джеффом - це занадто просто "припустити" сферу застосування при читанні заяви переключення через стиль відступу, яким користується більшість людей. Мій власний стиль полягає у тому, щоб завжди відкривати нову область для кожного випадку / за замовчуванням, якщо він довший, ніж один рядок.
Ставки

39
workmad3 - Чи можете ви знайти мені будь-який компілятор C ++, який створить новий фрейм стека, якщо ви не оголосите жодних нових змінних? Ви мене коротко хвилювались, але жоден з G ++ 3.1, Visual C ++ 7 або Intel C ++ 8 не створить будь-який код для нових областей, де ви не оголосите жодних змінних.
Кріс Джефферсон

10
@ workmad3 введення нового блоку фігурних дужок не спричиняє новий кадр стека stackoverflow.com/questions/2759371/…
MTVS

3
@TallJef Я не знаю, про які "старі часи" ти йдеш. Я ніколи не стикався з компілятором, де протягом 40 років весь стек для методу не виділяється при введенні методу.
користувач207421

331

Це питання буде спочатку був позначений як [C] і [C ++] в той же час. Оригінальний код дійсно недійсний як для C, так і для C ++, але з зовсім інших непов'язаних причин.

  • У C ++ цей код недійсний, оскільки case ANOTHER_VAL:мітка стрибає в область змінної, newValминаючи її ініціалізацію. Стрибки, які обходять ініціалізацію автоматичних об'єктів, незаконні в C ++. Ця сторона питання правильно вирішена більшістю відповідей.

  • Однак у мові C обхід змінної ініціалізації не є помилкою. Стрибок у область змінної над її ініціалізацією є законним у C. Це просто означає, що змінна залишається неініціалізованою. Оригінальний код не збирається в C з зовсім іншої причини. Мітка case VAL:в оригінальному коді додається до декларації змінної newVal. У мові С декларації не є твердженнями. Вони не можуть бути марковані. І саме це викликає помилку, коли цей код інтерпретується як код C.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }

Додавання додаткового {}блоку виправляє і проблеми C ++, і C, хоча ці проблеми бувають дуже різними. З боку C ++ він обмежує область застосування newVal, переконуючись, що case ANOTHER_VAL:більше не перескакує в цю область, що виключає проблему C ++. З боку С, що додатково {}вводить складений вислів, таким чином, робить case VAL:мітку застосованою до оператора, що усуває проблему C.

  • У випадку C проблему можна легко вирішити без {}. Просто додайте порожню заяву після case VAL:мітки, і код стане дійсним

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }

    Зауважте, що хоча він тепер дійсний з точки зору С, він залишається недійсним з точки зору C ++.

  • Симетрично у випадку C ++ проблему можна легко вирішити без {}. Просто видаліть ініціалізатор із змінної декларації, і код набуде чинності

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }

    Зауважте, що хоча він тепер дійсний з точки зору C ++, він залишається недійсним з точки зору C.


4
@AnT: Я розумію, чому той, що виправляє C ++, не застосовується для C; однак я не можу зрозуміти, як воно виправляє проблему C ++ пропускання ініціалізації в першу чергу? Чи не все-таки це пропустить декларацію та призначення, newValколи вона перестрибує ANOTHER_VAL?
legends2k

13
@ legends2k: Так, це все ще пропускає його. Однак, коли я кажу "вона виправляє проблему", я маю на увазі, що вона виправляє помилку компілятора C ++ . У C ++ пропускати скалярну декларацію з ініціалізатором заборонено , але пропустити скалярну заяву без ініціалізатора цілком чудово . У case ANOTHER_VAL:змінній точці newValвидно, але з невизначеним значенням.
ANT

3
Захоплююче. Це питання я знайшов після прочитання §A9.3: Compound Statementз K&R C (друге видання). Запис згадував технічне визначення складеної заяви, яка є {declaration-list[opt] statement-list[opt]}. Розгублений, бо я вважав, що декларація БЕЗ висловлювання, я подивився на це і негайно знайшов це запитання, наприклад, коли зазначена невідповідність стає очевидною і фактично порушує програму. Я вважаю, що іншим рішенням (для C) було б поставити ще одне твердження (можливо, нульове твердження?) Перед декларацією, щоб задовольнити мітку-заяву .
Бреден Кращий

На жаль, я щойно помітив, що рішення, яке я запропонував з нулем, вже є у вашій відповіді. Неважливо, значить.
Бреден Кращий

3
Варто зазначити, що виправлення додавання порожнього оператора працює лише для C99 і далі. У C89 змінні повинні бути оголошені на початку їхнього блоку, що додається.
Артур Такка

136

Добре. Тільки для уточнення цього строго немає нічого спільного з декларацією. Це стосується лише "переходу через ініціалізацію" (ISO C ++ '03 6.7 / 3)

Багато публікацій тут згадували, що перехід через декларацію може призвести до того, що змінна "не буде оголошена". Це не правда. Об'єкт POD може бути оголошений без ініціалізатора, але він матиме невизначене значення. Наприклад:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Якщо об'єктом є не-POD або агрегат, компілятор неявно додає ініціалізатор, і тому перейти через таку заяву неможливо:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

Це обмеження не обмежується оператором перемикання. Також помилка використання "goto" для переходу через ініціалізацію:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

Трохи дрібниць в тому, що це різниця між C ++ і C. У C це не помилка перейти через ініціалізацію.

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


2
"Помилка переходу через ініціалізацію" ??? Не з моєю GCC. Це може дати попередження "j може бути використане неітіалізованим", коли використовується j нижче мітки, але помилки немає. Однак у разі перемикання виникає помилка (сильна помилка, а не слабке попередження).
Mecki

9
@Mecki: Це незаконно в C ++. ISO C ++ '03 - 6.7 / 3: "... Програма, яка переходить з точки, коли локальна змінна з автоматичною тривалістю зберігання не входить в область, до якої вона знаходиться в області, є неправильною формою, якщо змінна не має тип POD (3.9) і оголошується без ініціалізатора (8.5). "
Річард Корден

1
Так, але це не незаконно в С (принаймні, gcc каже, що це не так). j буде неініціалізованим (мати деяке випадкове число), але компілятор компілює його. Однак у випадку оператора switch компілятор навіть не скомпілює його, і я не бачу різниці між випадком goto / label та випадком перемикача.
Mecki

8
@Mecki: Взагалі поведінка одного компілятора не обов'язково відображає те, що фактично дозволено мовою. Я перевірив і C'90, і C'99, і обидва стандарти включають приклад зі стрибком через ініціалізацію в операторі комутатора.
Річард Корден

38

Весь оператор перемикання знаходиться в одному обсязі. Щоб обійти це, зробіть це:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Зверніть увагу на дужки.


30

Прочитавши всі відповіді та ще кілька досліджень, я отримую кілька речей.

Case statements are only 'labels'

В C, згідно специфікації,

§ 6.8.1 Позначені виписки:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

У C не існує жодного застереження, яке дозволяє "декларувати мітку". Це просто не частина мови.

Тому

case 1: int x=10;
        printf(" x is %d",x);
break;

Це не складатиметься , див. Http://codepad.org/YiyLQTYw . GCC видає помилку:

label can only be a part of statement and declaration is not a statement

Навіть

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

це також не компілюється , див. http://codepad.org/BXnRD3bu . Тут я також отримую ту саму помилку.


В C ++, відповідно до специфікації,

мітка-декларація дозволена, але мітка-ініціалізація не дозволена.

Дивіться http://codepad.org/ZmQ0IyDG .


Рішення такої умови - два

  1. Або використовуйте нову область, використовуючи {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. Або використовуйте фіктивний вислів з міткою

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Заявіть змінну перед switch () та ініціалізуйте її з різними значеннями у заяві case, якщо вона відповідає вашій вимозі

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

Ще деякі речі із заявою перемикача

Ніколи не пишіть в комутаторі жодних тверджень, які не входять до жодної мітки, тому що вони ніколи не виконуються:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Дивіться http://codepad.org/PA1quYX3 .


2
Ви правильно описали проблему C. Але твердження, що в C ++ ініціалізація не дозволена, абсолютно не відповідає дійсності. Немає нічого поганого в маркованій ініціалізації в C ++. Те, що C ++ не дозволяє, це перехід через ініціалізацію змінної aв область змінної a. Отже, з точки зору С, проблеми є з case VAL:етикеткою, і ви її правильно описали. Але з точки зору C ++, проблема полягає в case ANOTHER_VAL:етикетці.
ANT

У C ++, на відміну від C, декларації - це підмножина висловлювань.
Кіт Томпсон

20

Ви не можете цього зробити, оскільки caseмітки - це фактично лише пункти входу до блоку, що містить.

Найяскравіше це проілюстровано пристроєм Даффа . Ось код із Вікіпедії:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    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);
    }
}

Зауважте, як caseмітки повністю ігнорують межі блоку. Так, це зло. Але тому ваш приклад коду не працює. Перехід до caseмітки такий же, як і використання goto, тому вам не дозволяється переходити через локальну змінну з конструктором.

Як зазначено в кількох інших плакатах, вам потрібно вкласти власний блок:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

1
Ця реалізація пристрою Даффа має помилку, що робить його надзвичайно повільним: count є тип int, тому% повинні виконати реальну операцію поділу / модуля. Зробіть підрахунок неподписаним (а ще краще, завжди використовуйте size_t для підрахунків / індексів), і проблема усувається.
R .. GitHub СТОП ДОПОМОГАЙТЕ

1
@R ..: Що ?! У системі доповнення двох, підписаність не впливає на модулі потужністю 2 (це просто А на нижній біт), і не впливає на поділи за потужностями 2, поки ваша архітектура процесора має арифметичну операцію з правою зміною. ( SARу x86, порівняно SHRз ненаписаними зрушеннями).
Кріс Єстер-Янг

@Chris: я вважаю, що він має на увазі, коли компілятор повинен враховувати негативні значення, де "просто А на нижній біт" не втримується; наприклад, -1% 8 дає -1 для системи доповнення цих двох за допомогою g ++ (знак у цьому випадку є реалізацією, визначеною у 5,6 / 4).

3
@Chris: Я згоден з вами, що R перебільшує вплив; Я бачив лише ваш коментар і знав, що просте І не вистачало.

1
Також варто відзначити, що оригінальний код Вікіпедії призначений для надсилання даних на виведений на карту пам'яті, який тут виглядає дивним, оскільки він не згадується, і кожен байт копіюється в одне і те ж місце "до". Можна було б обійти це, додавши в файл postfix ++ або згадуючи випадок використання, призначений для IO, відображеного на пам'ять. Зовсім периферійне до початкового питання :-).
Петро

16

Більшість відповідей поки що помилкові в одному відношенні: ви можете оголосити змінні після оператора справи, але ви не можете їх ініціалізувати:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Як вже було сказано раніше, приємним способом цього є використання дужок для створення сфери для вашої справи.


1
Пане 32, ви неправильно зрозуміли, у чому полягає ваша помилка: так, це не збирається збирати, але не тому, що ви оголошуєте змінну всередині комутатора. Помилка полягає в тому, що ви намагаєтесь оголосити змінну після заяви, що є незаконним у C.
MrZebra

1
Зараз дні, які є законними у c90 та новішій версії c
Jeegar Patel,

12

Мій улюблений трюк злого перемикання - використовувати if (0), щоб пропустити мітку небажаного корпусу.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

Але дуже зле.


Дуже хороший. Приклад того, чому: випадок 0 і випадок 1 можуть, наприклад, ініціалізувати змінну по-іншому, що використовується у випадку 2.
hlovdal

1
Якщо ви хочете, щоб і випадок 0, і випадок 1 перейшли через випадок 2. (без випадку 0, який проходить через випадок 1) Не знаю, чи справді це корисно, але впевнено працює.
Петруза

1
Ви можете просто перейти до потрібної мітки, gotoне
обтураючи


7

Ви можете оголосити змінні в операторі комутатора, якщо ви запустили новий блок:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

Причина полягає в тому, щоб виділити (і відшкодувати) простір у стеку для зберігання локальної змінної.


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

6

Поміркуйте:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

За відсутності заяв про перерви іноді newVal отримують заявлену двічі, і ви не знаєте, чи буде це до моменту виконання. Я здогадуюсь, що обмеження відбувається через такого роду плутанини. Якою була б сфера застосування новогоVal? Конвенція передбачає, що це буде весь блок комутаторів (між дужками).

Я не програміст на C ++, але на C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Добре працює. Оголошення змінної всередині блоку комутаторів є прекрасним. Декларація після охоронця справи - ні.


3
@ Mr.32: насправді ваш приклад показує, що printf не виконується, але в цьому випадку, int x - це не оператор, а декларація, оголошено x, простір для нього зарезервовано кожного разу, коли середовище функції зберігається, дивіться: codepad.org/4E9Zuz1e
Петруза

Я очікував, що це знайду, читаючи заголовок питання, оскільки питання не про декларування змінних у мітках "case:", а в операторах переключення. І тільки ви (і VictorH, наголошуючи на вашій відповіді) насправді говорили про змінні в операторах переключення.
cesss

4

Весь розділ комутатора - це єдиний контекст оголошення. Ви не можете оголосити змінну в такому випадку. Спробуйте це замість цього:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

Змінна може бути оголошена, але її неможливо ініціалізувати.
Річард Корден

@Richard Corden Я впевнений, що ініціалізація спрацює. Ви все ще стверджуєте, що його неможливо ініціалізувати?
chux

3

Якщо у вашому коді написано "int newVal = 42", ви обгрунтовано очікуєте, що newVal ніколи не буде ініціалізований. Але якщо ви перейдете до цього твердження (що саме ви робите), то саме так і станеться - newVal є в області дії, але він не був призначений.

Якщо це дійсно ви мали намір статися, то мова вимагає зробити це явним, сказавши "int newVal; newVal = 42;". В іншому випадку ви можете обмежити сферу застосування newVal єдиним випадком, що скоріше того, що ви хотіли.

Це може прояснити речі, якщо ви розглянете той же приклад, але з "const int newVal = 42;"


3

Я просто хотів би підкреслити тонкий «s точку . Конструкція комутатора створює цілу сферу дії для першокласного громадянина. Отже, можливо оголосити (і ініціалізувати) змінну в операторі перемикача перед першою міткою випадку, без додаткової пари дужок:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

-1 тут int newVal = 42; не буде ніколи виконаний. дивіться цей codepad.org/PA1quYX3
Jeegar Patel

4
декларація int newVal буде виконана, але не = 42призначення.
Петруза

3

Поки що відповіді були для C ++.

Для C ++ ви не можете перейти через ініціалізацію. Ви можете в C. Однак, у C декларація не є твердженням, а на мітках регістрів повинні слідувати заяви.

Отже, дійсне (але потворне) C, недійсне C ++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

З іншого боку, у C ++ декларація є твердженням, тому дійсне наступне C ++, недійсне C

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

1
Другий приклад НЕ дійсний C ++ (тест з vc2010 та gcc 4.6.1 C ++ не дозволяє пропустити частину ініціалізації. Gcc повідомлення про помилку: перехресна ініціалізація 'int i'
zhaorufei

3

Цікаво, що це добре:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... але це не так:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

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


1
По-перше, це не добре для gcc 4.2: "помилка: очікуване вираз перед" int "". Як кажуть Петро і Містер.32, "випадок 0:; int j; ..." і "випадок 0:; int j = 7; ..." виконують обидва. Проблема в C полягає лише в тому, що "case <label>: заява" не є синтаксисом C.
сумнівним

3

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

Оригінальний код, про який йдеться:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

Насправді є 2 питання:

1. Чому я можу оголосити змінну після case мітки?

Це тому, що в C ++ мітка повинна бути у формі:

N3337 6.1 / 1

мітка-заява:

...

  • attribute-specier-seqopt case constant-expression :statement

...

А в C++ декларації заява також розглядається як заява (на відміну від C):

N3337 6/1:

заява :

...

декларація-заява

...

2. Чому я можу перейти через змінну декларацію і потім використати її?

Тому що: N3337 6.7 / 3

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

від точки, коли змінна з автоматичною тривалістю зберігання не перебуває в області, до точки, де вона знаходиться в області сфери, неправильно формується, якщо змінна не має скалярного типу , типу класу з тривіальним конструктором за замовчуванням і тривіальним деструктором, версією, що має кваліфікацію cv одного з цих типів або масив одного з попередніх типів і оголошується без ініціалізатора (8.5).

Оскільки kє скалярного типу і не ініціалізований у точці декларування, можливо перескакувати його оголошення. Це семантично еквівалентно:

goto label;

int x;

label:
cout << x << endl;

Однак це було б неможливо, якби xбуло ініціалізовано в точці декларації:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

1

Нові змінні можуть бути задекларовані лише в області блоку. Вам потрібно написати щось подібне:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Звичайно, newVal має лише сферу в рамках брекетів ...

Ура, Ральфе


1

switchБлок не є такою ж , як послідовність if/else ifблоків. Я здивований, що жодна інша відповідь не пояснює це чітко.

Розглянемо це switchтвердження:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Це може дивуватися, але компілятор не сприйме це як простий if/else if. Він створить такий код:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

Ці caseзаяви перетворюються в ярлики , а потім викликаються goto. Дужки створюють нову область застосування, і зараз легко зрозуміти, чому ви не можете оголосити дві змінні з тим самим іменем у межахswitch блоці.

Це може виглядати дивно, але необхідно підтримувати падіння (тобто не використовувати breakдля того, щоб виконання продовжувалося до наступного case).


0

Я вважаю, що проблема в тому, що ця заява пропущена, і ви намагалися використовувати var в іншому місці, вона не буде оголошена.


0

newVal існує у всій області вимикача, але ініціалізується лише в разі удару кінцівки VAL. Якщо ви створюєте блок навколо коду в VAL, це повинно бути добре.


0

Стандарт C ++ має: Можна перенести в блок, але не таким чином, що обходить декларації з ініціалізацією. Програма, яка переходить з точки, коли локальна змінна з автоматичною тривалістю зберігання не перебуває в області, до точки, де вона знаходиться в області дії, неправильно формується, якщо змінна не має типу POD (3.9) і не оголошена без ініціалізатора (8.5).

Код для ілюстрації цього правила:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

Код для показу ефекту ініціалізатора:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

0

Виявляється, анонімні об'єкти можуть бути оголошені або створені в операторі вимикача з тієї причини, що їх не можна посилати, і як такі не можуть перейти до наступного випадку. Розглянемо цей приклад компіляції на GCC 4.5.3 та Visual Studio 2008 (можливо, це питання відповідності тому, тому експерти, будь ласка, зважують)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}

Якщо ви збираєтесь це проголосувати, поясніть, чому. Мені цікаво дізнатися, чому створення анонімного об’єкта видається винятком.
Олумід

1
не DV, але: Все питання стосується оголошення / обсягу названих змінних. Тимчасовий ("анонімний об'єкт" не є терміном) не є названою змінною, не є декларацією, а також не підпадає під сферу застосування (якщо не пов'язане з constпосиланням із власною сферою застосування). Це вираз, який живе і вмирає в межах своєї заяви (де б це не було). Тому це абсолютно не має значення.
підкреслюй_d

Foo();не є декларацією; питання стосується декларацій.
ММ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.