Які ознаки ініціалізації хрестів?


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 не може бути визначений у багатовипадковому операторі перемикання.


1
Я розумію, враховуючи відповіді нижче, для пункту 3 така помилка є надмірним обмеженням c ++. Якщо r не використовується після мітки, це не впливає (навіть якщо в прикладі тут використовується r, його можна видалити у випадку 2, і компілятор дасть ту саму помилку). Кращим доказом є те, що це дозволено в C, і навіть в C11.
calandoa

Відповіді:


95

Версія з 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.03.10

10
Ну, мій g ++ ні. Перевірте ще раз або оновіть компілятор.
avakar

дякую, це було мені корисно. Я думаю, що компілятор 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 до мітки справи вважається стрибком у цьому відношенні.


1
Ласкаво просимо до Stack Overflow. Ви повинні надати джерела для своїх цитат, це C ++ 03: 6.7 / 3. Також трапляється той самий абзац, який я цитував у своїй відповіді.
avakar

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.