¿Saltar sobre una inicialización variable está mal formado o causa un comportamiento indefinido?

17

Considera este código:

void foo()
{
    goto bar;
    int x = 0;
    bar: ;
}

GCC y Clang lo rechazan porque el salto a bar:omite la inicialización variable. MSVC no se queja en absoluto (excepto que usar xdespués bar:causa una advertencia).

Podemos hacer algo similar con un switch:

void foo()
{
    switch (0)
    {
        int x = 0;
        case 0: ;
    }
}

Ahora los tres compiladores emiten errores .

¿Están esos fragmentos mal formados? ¿O causan UB?

Solía ​​pensar que ambos estaban mal formados, pero no puedo encontrar las partes reveladoras del estándar. [stmt.goto] no dice nada al respecto, y tampoco [stmt.select] .

HolyBlackCat
fuente
1
El problema sería más trivial si lo usa xdespués del salto.
Jarod42
1
no es el estándar, pero aquí se puede encontrar información al respecto: en.cppreference.com/w/cpp/language/goto en particular: "Si la transferencia de control entra en el alcance de cualquier variable automática (por ejemplo, saltando hacia adelante sobre una declaración ), el programa está mal formado (no se puede compilar), a menos que ... "
idclev 463035818
Agregue la /permissive-bandera a MSVC y también se quejará. Sin embargo, no sé si el comportamiento de MSVC sin ese indicador está bien definido (supongo que sí, de lo contrario, ¿por qué lo permitirían?).
nogal
@walnut "de lo contrario, ¿por qué lo permitirían?" Posiblemente por compatibilidad con versiones anteriores, o porque no les importa demasiado el estándar. Todos los compiladores principales no se ajustan al estándar en la configuración predeterminada.
HolyBlackCat

Respuestas:

20

Está mal formado cuando la inicialización no es vacía.

[stmt.dcl]

3 Es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización (incluidas las que se encuentran en condiciones y enunciados init). Un programa que salta desde un punto donde una variable con duración de almacenamiento automática no está dentro del alcance hasta un punto donde está dentro del alcance está mal formado a menos que la variable tenga una inicialización vacía ([basic.life]). En tal caso, las variables con inicialización vacía se construyen en el orden de su declaración.

El inicializador hace que la inicialización no sea vacía. Por el contrario, esto

void foo()
{
    goto bar;
    int x; // no initializer
    bar: ;
}

Estaría bien formado. Aunque xse aplicarían las advertencias habituales sobre el uso con un valor indeterminado.

StoryTeller - Unslander Monica
fuente
¿las declaraciones de variables no tienen que ser lo primero en un alcance de todos modos?
Cruncher
44
@Cruncher: C89 lo requiere. C ++ nunca lo hizo, y tampoco lo hace la C moderna.
StoryTeller - Unslander Monica
3

De la declaración goto :

Si la transferencia de control entra en el alcance de cualquier variable automática (por ejemplo, saltando hacia adelante sobre una declaración de declaración), el programa está mal formado (no se puede compilar), a menos que todas las variables cuyo alcance se haya ingresado tengan

  1. tipos escalares declarados sin inicializadores
  2. tipos de clase con constructores triviales predeterminados y destructores triviales declarados sin inicializadores
  3. versiones calificadas para cv de uno de los anteriores
  4. matrices de uno de los anteriores
Buscador de la verdad
fuente