¿Cuáles son los signos de inicialización de cruces?

84

Considere el siguiente código:

#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 ++ se queja crosses initialization of 'int r'. Mis preguntas son:

  1. ¿Qué es crosses initialization?
  2. ¿Por qué el primer inicializador x + ypasa la compilación, pero el último falla?
  3. ¿Cuáles son los problemas de los llamados crosses initialization?

Sé que debería usar corchetes para especificar el alcance de r, pero quiero saber por qué, por ejemplo, por qué no se pudo definir no POD en una declaración de cambio de varios casos.

Jichao
fuente
1
Según entiendo, dadas las respuestas a continuación, el punto 3 es que este error es una restricción excesiva de c ++. Si r no se usa después de la etiqueta, no hay impacto (incluso si el ejemplo aquí usa r, se puede eliminar en el caso 2 y el compilador daría el mismo error). La mejor prueba es que está permitido en C, e incluso en C11.
calandoa
Posible duplicado del Error: Ir a la etiqueta del caso
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

95

La versión con int r = x + y;tampoco se compilará.

El problema es que es posible rllegar al alcance sin que se ejecute su inicializador. El código se compilaría bien si eliminara el inicializador por completo (es decir, la línea se leería int r;).

Lo mejor que puede hacer es limitar el alcance de la variable. De esa forma, satisfará tanto al compilador como al lector.

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

El Estándar dice (6.7 / 3):

Es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización. Un programa que salta desde un punto donde una variable local con duración de almacenamiento automático no está dentro del alcance a un punto en el que está dentro del alcance está mal formado a menos que la variable tenga el tipo POD (3.9) y se declare sin un inicializador (8.5).

avakar
fuente
Pero mi G ++ lo permite int r = x + y.
Jichao
10
Bueno, mi g ++ no lo hace. Vuelva a verificar o actualice el compilador.
avakar
gracias, eso me ayudó. Creo que el compilador de C ni siquiera permite que la declaración venga después de algún código. Aparentemente, C99 lo permite, aunque ... stackoverflow.com/questions/7859424/…
m-ric
36

Debes poner el contenido de caseentre paréntesis para darle alcance, de esa manera puedes declarar variables locales dentro de él:

switch(i) {
    case 1:
        {
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
        }
        break;
    case 2:
        ...
        break;
};
Péter Török
fuente
3

Es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización. Un programa que salta desde un punto donde una variable local con duración de almacenamiento automático no está dentro del alcance a un punto en el que está dentro del alcance está mal formado a menos que la variable tenga un tipo POD y se declare sin un inicializador.

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

La transferencia de la condición de una declaración de cambio a una etiqueta de caso se considera un salto en este sentido.

Ashish Yadav
fuente
1
Bienvenido a Stack Overflow. Debe proporcionar fuentes para sus citas, esto es C ++ 03: 6.7 / 3. También resulta ser el mismo párrafo que cité en mi respuesta.
Avakar
0

Le sugiero que promueva su rvariable antes de la switchdeclaración. Si desea utilizar una variable en todos los casebloques (o el mismo nombre de variable pero diferentes usos), defínela antes de la declaración de cambio:

#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 de los beneficios es que el compilador no tiene que realizar una asignación local (es decir, empujar hacia la pila) en cada casebloque.

Un inconveniente de este enfoque es cuando los casos "caen" en otros casos (es decir, sin usar break), ya que la variable tendrá un valor anterior.

Thomas Matthews
fuente
2
Sugeriría hacerlo al revés. El compilador no tiene que realizar una "asignación local" en ningún caso (y en la práctica no lo hará).
Avakar