Jakie są oznaki inicjalizacji krzyżyków?


84

Rozważ następujący kod:

#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 ++ narzeka crosses initialization of 'int r'. Moje pytania to:

  1. Co to jest crosses initialization?
  2. Dlaczego pierwszy inicjator x + yprzechodzi kompilację, a drugi zawiódł?
  3. Jakie są problemy z tzw crosses initialization?

Wiem, że powinienem użyć nawiasów, aby określić zakres r, ale chcę wiedzieć, dlaczego, na przykład, dlaczego nie można zdefiniować POD-POD w instrukcji przełącznika wielozadaniowego.


1
Rozumiem, biorąc pod uwagę poniższe odpowiedzi, dla punktu 3, że ten błąd jest nadmiernym ograniczeniem c ++. Jeśli r nie jest używany po etykiecie, nie ma wpływu (nawet jeśli w przykładzie tutaj użyto r, można je usunąć w przypadku 2, a kompilator poda ten sam błąd). Lepszym dowodem jest to, że jest to dozwolone w C, a nawet w C11.
calandoa

Odpowiedzi:


95

Wersja z int r = x + y;nie będzie się również kompilować.

Problem polega na tym, że możliwe rjest dojście do zakresu bez wykonywania jego inicjatora. Kod skompilowałby się dobrze, gdybyś całkowicie usunął inicjalizator (tj. Wiersz zostałby odczytany int r;).

Najlepszą rzeczą, jaką możesz zrobić, jest ograniczenie zakresu zmiennej. W ten sposób zadowoli zarówno kompilator, jak i czytelnika.

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

Norma mówi (6,7 / 3):

Możliwe jest przeniesienie do bloku, ale nie w sposób omijający deklaracje przy inicjalizacji. Program, który przeskakuje z punktu, w którym zmienna lokalna z automatycznym czasem przechowywania nie znajduje się w zakresie, do punktu, w którym jest w zakresie, jest źle sformułowany, chyba że zmienna ma typ POD (3.9) i jest zadeklarowana bez inicjatora (8.5).


Ale mój G ++ pozwala int r = x + y.
Jichao

10
Cóż, moje g ++ nie. Sprawdź ponownie lub zaktualizuj kompilator.
avakar

dzięki, to było dla mnie pomocne. Myślę, że kompilator C nie pozwala nawet na umieszczenie deklaracji po jakimś kodzie. Najwyraźniej C99 na to pozwala ... stackoverflow.com/questions/7859424/
m-ric

36

Powinieneś umieścić zawartość casew nawiasach, aby nadać jej zakres, w ten sposób możesz zadeklarować w nim zmienne lokalne:

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

3

Możliwe jest przeniesienie do bloku, ale nie w sposób omijający deklaracje przy inicjalizacji. Program, który przeskakuje z punktu, w którym zmienna lokalna z automatycznym czasem przechowywania nie znajduje się w zakresie, do punktu, w którym jest w zakresie, jest źle sformułowany, chyba że zmienna ma typ POD i jest zadeklarowana bez inicjatora.

[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]

Przejście od warunku instrukcji zmiany do etykiety przypadku jest uważane za skok w tym względzie.


1
Witamy w Stack Overflow. Powinieneś podać źródła swoich cytatów, to jest C ++ 03: 6.7 / 3. Tak się składa, że ​​jest to ten sam akapit, który przytoczyłem w odpowiedzi.
avakar

0

Proponuję promować swoją rzmienną przed switchstwierdzeniem. Jeśli chcesz użyć zmiennej w caseblokach (lub tej samej nazwy zmiennej, ale różnych zastosowań), zdefiniuj ją przed instrukcją 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;
    };
}

Jedną z korzyści jest to, że kompilator nie musi wykonywać lokalnej alokacji (czyli wypychania na stos) w każdym casebloku.

Wadą tego podejścia jest sytuacja, gdy obserwacje „wpadają” w inne przypadki (tj. Bez użycia break), ponieważ zmienna będzie miała poprzednią wartość.


2
Proponuję zrobić to na odwrót. W żadnym przypadku kompilator nie musi wykonywać „lokalnej alokacji” (iw praktyce nie będzie).
avakar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.