Quali sono i segni di inizializzazione delle croci?


84

Considera il codice seguente:

#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 ++ si lamenta crosses initialization of 'int r'. Le mie domande sono:

  1. Che cos'è crosses initialization?
  2. Perché il primo inizializzatore x + ysupera la compilazione, ma il secondo non è riuscito?
  3. Quali sono i problemi dei cosiddetti crosses initialization?

So che dovrei usare le parentesi per specificare l'ambito di r, ma voglio sapere perché, ad esempio perché non è stato possibile definire non POD in un'istruzione switch multi-case.


1
La mia comprensione, date le risposte seguenti, per il punto 3 è che questo errore è una restrizione eccessiva di c ++. Se r non è usato dopo l'etichetta, non c'è alcun impatto (anche se l'esempio qui usa r, può essere rimosso nel caso 2 e il compilatore darebbe lo stesso errore). La prova migliore è che è consentito in C e anche in C11.
calandoa

Risposte:


96

Anche la versione con int r = x + y;non verrà compilata.

Il problema è che è possibile rentrare nello scope senza che il suo inizializzatore venga eseguito. Il codice si compilerebbe bene se rimuovessi completamente l'inizializzatore (cioè la riga verrebbe letta int r;).

La cosa migliore che puoi fare è limitare l'ambito della variabile. In questo modo soddisferai sia il compilatore che il lettore.

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

Lo Standard dice (6.7 / 3):

È possibile trasferire in un blocco, ma non in un modo che ignori le dichiarazioni con inizializzazione. Un programma che salta da un punto in cui una variabile locale con durata di memorizzazione automatica non è nello scope a un punto in cui è nello scope è mal formato a meno che la variabile non abbia il tipo POD (3.9) e venga dichiarata senza un inizializzatore (8.5).


Ma il mio G ++ lo consente int r = x + y.
Jichao

10
Ebbene, il mio g ++ no. Controlla di nuovo o aggiorna il compilatore.
avakar

grazie, mi è stato utile. Penso che il compilatore C non permetta nemmeno che la dichiarazione venga dopo un po 'di codice. Apparentemente C99 lo consente però ... stackoverflow.com/questions/7859424/…
m-ric

36

Dovresti mettere il contenuto di casetra parentesi per dargli un ambito, in questo modo puoi dichiarare le variabili locali al suo interno:

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

3

È possibile trasferire in un blocco, ma non in un modo che ignori le dichiarazioni con inizializzazione. Un programma che salta da un punto in cui una variabile locale con durata di memorizzazione automatica non è nell'ambito di applicazione a un punto in cui è nell'ambito è mal formato a meno che la variabile non sia di tipo POD e venga dichiarata senza un inizializzatore.

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

Il trasferimento dalla condizione di un'istruzione switch a un'etichetta di caso è considerato un salto in questo senso.


1
Benvenuto in Stack Overflow. Dovresti fornire le fonti per le tue citazioni, questo è C ++ 03: 6.7 / 3. Capita anche che sia lo stesso paragrafo che ho citato nella mia risposta.
avakar

0

Ti suggerisco di promuovere la tua rvariabile prima della switchdichiarazione. Se si desidera utilizzare una variabile tra i caseblocchi (o lo stesso nome di variabile ma usi diversi), definirla prima dell'istruzione 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;
    };
}

Uno dei vantaggi è che il compilatore non deve eseguire l'allocazione locale (ovvero l'inserimento nello stack) in ogni caseblocco.

Uno svantaggio di questo approccio è quando i casi "cadono" in altri casi (cioè senza utilizzare break), poiché la variabile avrà un valore precedente.


2
Suggerirei di farlo al contrario. Il compilatore non deve eseguire l '"allocazione locale" in entrambi i casi (e in pratica non lo farà).
avakar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.