Le saut par-dessus une initialisation variable est-il mal formé ou provoque-t-il un comportement indéfini?

17

Considérez ce code:

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

GCC et Clang le rejettent , car le saut pour bar:contourne l'initialisation des variables. MSVC ne se plaint pas du tout (sauf si l'utilisation xaprès bar:provoque un avertissement).

Nous pouvons faire une chose similaire avec switch:

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

Désormais, les trois compilateurs émettent des erreurs .

Ces extraits sont-ils mal formés? Ou causent-ils de l'UB?

Je pensais que les deux étaient mal formés, mais je ne trouve pas les parties révélatrices de la norme. [stmt.goto] ne dit rien à ce sujet, pas plus que [stmt.select] .

HolyBlackCat
la source
1
Le problème serait plus trivial si vous utilisez xaprès le saut.
Jarod42
1
pas la norme, mais ici on peut trouver des informations à ce sujet: en.cppreference.com/w/cpp/language/goto en particulier: "Si le transfert de contrôle entre dans le champ d’application de variables automatiques (par exemple en sautant par dessus une déclaration ), le programme est mal formé (ne peut pas être compilé), à moins que ... "
idclev 463035818
Ajoutez le /permissive-drapeau à MSVC et il se plaindra également. Je ne sais pas si le comportement de MSVC sans cet indicateur est bien défini (je suppose que oui, sinon pourquoi le permettrait-il?).
noyer
@walnut "sinon pourquoi le permettraient-ils" Peut-être pour une compatibilité descendante, ou parce qu'ils ne se soucient pas trop de la norme. Tous les principaux compilateurs ne sont pas conformes à la norme dans les paramètres par défaut.
HolyBlackCat

Réponses:

20

Il est mal formé lorsque l'initialisation est non vide.

[stmt.dcl]

3 Il est possible de transférer dans un bloc, mais pas d'une manière qui contourne les déclarations avec initialisation (y compris celles dans les conditions et les instructions init). Un programme qui passe d'un point où une variable avec une durée de stockage automatique n'est pas dans la portée à un point où elle est dans la portée est mal formé, sauf si la variable a une initialisation vide ([basic.life]). Dans un tel cas, les variables à initialisation vide sont construites dans l'ordre de leur déclaration.

L'initialiseur rend l'initialisation non vide. En revanche, cette

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

serait bien formé. Bien que les mises en garde habituelles concernant l'utilisation xavec une valeur indéterminée s'appliquent.

Conteur - Unslander Monica
la source
les déclarations de variables ne doivent-elles pas être la première chose dans une étendue de toute façon?
Cruncher
4
@Cruncher - C89 en avait besoin. Le C ++ ne l'a jamais fait, et le C moderne non plus.
StoryTeller - Unslander Monica
3

De la déclaration goto :

Si le transfert de contrôle entre dans le champ d'application de variables automatiques (par exemple en sautant par-dessus une déclaration), le programme est mal formé (ne peut pas être compilé), sauf si toutes les variables dont le champ d'application est entré ont

  1. types scalaires déclarés sans initialiseurs
  2. types de classe avec des constructeurs par défaut triviaux et des destructeurs triviaux déclarés sans initialiseurs
  3. versions certifiées cv de l'une des versions ci-dessus
  4. tableaux de l'un des éléments ci-dessus
TruthSeeker
la source