Какие признаки инициализации крестиков?

84

Рассмотрим следующий код:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
    switch(i) {
        case 1:
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

G ++ жалуется crosses initialization of 'int r'. Мои вопросы:

  1. Что есть crosses initialization?
  2. Почему первый инициализатор x + yпрошел компиляцию, а второй не прошел?
  3. Какие проблемы так называемые crosses initialization?

Я знаю, что должен использовать скобки, чтобы указать объем r, но я хочу знать, почему, например, почему не-POD нельзя определить в операторе переключения с несколькими регистрами.

Jichao
источник
1
Насколько я понимаю, учитывая приведенные ниже ответы для пункта 3, эта ошибка является чрезмерным ограничением C ++. Если r не используется после метки, это не влияет (даже если в примере здесь используется r, его можно удалить в случае 2, и компилятор выдаст ту же ошибку). Лучшее доказательство - то, что это разрешено в C и даже в C11.
Calandoa
Возможный дубликат ошибки: Перейти к ярлыку случая
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

96

Версия с int r = x + y;тоже не компилируется.

Проблема в том, что можно rпопасть в область видимости без выполнения ее инициализатора. Код будет нормально компилироваться, если вы полностью удалите инициализатор (т.е. строка будет читаться int r;).

Лучшее, что вы можете сделать, - это ограничить область действия переменной. Таким образом вы удовлетворите и компилятор, и читателя.

switch(i)
{
case 1:
    {
        int r = 1;
        cout << r;
    }
    break;
case 2:
    {
        int r = x - y;
        cout << r;
    }
    break;
};

Стандарт говорит (6.7 / 3):

Можно передать в блок, но не в обход объявлений с инициализацией. Программа, которая перескакивает из точки, в которой локальная переменная с автоматической продолжительностью хранения не находится в области видимости, к точке, где она находится в области видимости, плохо сформирована, если только переменная не имеет тип POD (3.9) и объявлена ​​без инициализатора (8.5).

авакар
источник
Но мой G ++ позволяет int r = x + y.
Jichao 06
10
Ну, мой g ++ - нет. Проверьте еще раз или обновите компилятор.
avakar 06
спасибо, это было полезно для меня. Я думаю, что компилятор C даже не допускает объявления после некоторого кода. Судя по всему, C99 позволяет это, хотя ... stackoverflow.com/questions/7859424/…
m-ric
36

Вы должны поместить содержимое caseв скобки, чтобы дать ему область видимости, таким образом вы можете объявить внутри него локальные переменные:

switch(i) {
    case 1:
        {
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
        }
        break;
    case 2:
        ...
        break;
};
Петер Торок
источник
3

Можно передать в блок, но не в обход объявлений с инициализацией. Программа, которая перескакивает из точки, где локальная переменная с автоматической продолжительностью хранения не находится в области видимости, к точке, где она находится в области видимости, является плохо сформированной, если только переменная не имеет тип POD и не объявлена ​​без инициализатора.

[Example: Code:

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
}

--end example]

В этом отношении переход от условия оператора switch к метке case считается скачком.

Ашиш Ядав
источник
1
Добро пожаловать в Stack Overflow. Вы должны предоставить источники для цитат, это C ++ 03: 6.7 / 3. Это также тот же абзац, который я процитировал в своем ответе.
avakar 06
0

Я предлагаю вам продвигать вашу rпеременную до switchутверждения. Если вы хотите использовать переменную в caseблоках (или одно и то же имя переменной, но разные способы использования), определите ее перед оператором switch:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
// Define the variable before the switch.
    int r;
    switch(i) {
        case 1:
            r = x + y
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

Одно из преимуществ состоит в том, что компилятору не нужно выполнять локальное выделение (также называемое отправкой в ​​стек) в каждом caseблоке.

Недостатком этого подхода является то, что случаи «попадают» в другие случаи (т.е. без использования break), поскольку переменная будет иметь предыдущее значение.

Томас Мэтьюз
источник
2
Я бы предложил сделать наоборот. Компилятору не обязательно выполнять «локальное выделение» в любом случае (и на практике этого не произойдет).
avakar 06