O salto sobre uma inicialização variável está mal formado ou causa comportamento indefinido?

17

Considere este código:

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

O GCC e o Clang rejeitam , porque o salto para bar:ignora a inicialização da variável. O MSVC não reclama de nada (exceto usar xafter bar:causa um aviso).

Podemos fazer uma coisa semelhante com um switch:

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

Agora todos os três compiladores emitem erros .

Esses trechos estão mal formados? Ou eles causam UB?

Eu costumava pensar que ambos eram mal formados, mas não consigo encontrar as partes reveladoras do padrão. [stmt.goto] não diz nada sobre isso, nem [stmt.select] .

HolyBlackCat
fonte
11
O problema seria mais trivial se você usar xdepois do salto.
Jarod42 27/01
11
não é o padrão, mas aqui podemos encontrar algumas informações: en.cppreference.com/w/cpp/language/goto em particular: "Se a transferência de controle entrar no escopo de qualquer variável automática (por exemplo, saltando sobre uma declaração ), o programa está mal formado (não pode ser compilado), a menos que ... "
idclev 463035818
Adicione o /permissive-sinalizador ao MSVC e ele também irá reclamar. Não sei se o comportamento do MSVC sem esse sinalizador está bem definido (eu diria que sim, caso contrário, por que eles permitiriam?).
noz
@walnut "caso contrário, por que eles permitiriam?" Possivelmente para compatibilidade com versões anteriores, ou porque eles não se importam muito com o padrão. Todos os principais compiladores não estão em conformidade com o padrão nas configurações padrão.
HolyBlackCat 27/01

Respostas:

20

É mal formado quando a inicialização não é vazia.

[stmt.dcl]

3 É possível transferir para um bloco, mas não de uma maneira que ignore as declarações com a inicialização (incluindo as em condições e instruções init). Um programa que salta de um ponto em que uma variável com duração de armazenamento automático não está no escopo para um ponto em que está no escopo está mal formada, a menos que a variável tenha inicialização vazia ([basic.life]). Nesse caso, as variáveis ​​com inicialização vazia são construídas na ordem de sua declaração.

O inicializador torna a inicialização não vazia. Em contraste, isso

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

seria bem formado. Embora as advertências usuais sobre o uso xcom um valor indeterminado se apliquem.

Contador de Histórias - Monica Sem Calúnia
fonte
declarações de variáveis ​​não precisam ser a primeira coisa em um escopo?
Cruncher
4
@Cruncher - C89 exigia. O C ++ nunca foi, e nem o C moderno.
StoryTeller - Unslander Monica
3

Da declaração goto :

Se a transferência de controle entrar no escopo de qualquer variável automática (por exemplo, saltando sobre uma declaração), o programa está mal formado (não pode ser compilado), a menos que todas as variáveis ​​cujo escopo seja inserido tenham

  1. tipos escalares declarados sem inicializadores
  2. tipos de classe com construtores padrão triviais e destruidores triviais declarados sem inicializadores
  3. versões qualificadas para cv de uma das opções acima
  4. matrizes de uma das opções acima
TruthSeeker
fonte