4

I bumped against this error in some code, and after some experimenting I stumbled upon this weirdness - I get it for std::string, but not for int.

For std::string I get error C2362: initialization of 'unused' is skipped by 'goto label':

{   goto label;
    std::string unused;
label:;
}

For int I don't get any error, however:

{   goto label;
    int unused = 10;
label:;
}

Why the difference? Is it because std::string has a non-trivial destructor?

sashoalm
  • 75,001
  • 122
  • 434
  • 781

2 Answers2

5

This is covered in the draft C++ standard section 6.7 Declaration statement which says (emphasis mine):

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps87 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).

and provides the following example:

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
}

Although both cases should generate an error since your are bypassing an initialization in both cases, this however would have been fine:

goto label;
      int unused ;
label:

So Visual Studio is not correct here, both gcc and clang generate and error for this code, gcc says:

error:   crosses initialization of 'int unused'
       int unused = 10;
           ^

Of course Visual Studio can have extension like that as long as they document it but it is not portable to use such an extension, as I pointed out both clang and gcc generate an error for this.

We can find a rationale for why we don't want to jump across an initialization in defect report 467 which sought to have the same restriction added for local static variable (it was rejected):

[...]automatic variables, if not explicitly initialized, can have indeterminate (“garbage”) values, including trap representations, [...]

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 1
    Except that both of his examples have initialization, and should be illegal. If the second compiles, it is an error in the compiler. – James Kanze Apr 04 '14 at 13:26
  • @JamesKanze correct, I was just checking out which compiler allowed this ... apparently Visual Studio – Shafik Yaghmour Apr 04 '14 at 13:32
  • @ShafikYaghmour Visual Studio makes the most sensible thing here, I think, because `unused` is well - unused. That the standard doesn't make an exception for this case here is a mistake in the standard. – sashoalm Apr 04 '14 at 13:45
3

A compiler error. Both are illegal. What is not illegal, however, is:

{
    goto label;
    int unused;
    unused = 10;
label:
    ;
}

Both std::string unused; and int unused = 10; have initializers (a default constructor in the case of std::string), and you're not allowed to jump around a definition with an initializer. Jumping around one without an initializer is probably allowed avoid breaking code like:

switch ( something )
{
    int i;
case 0:
    i = x;
    // ...
    break;

case 1:
    i = y;
    //  ...
    break;
//  ...
}

I wouldn't call this good code, but it wouldn't surprise me to find it in older C, and C++ does try not to break these sort of things.

James Kanze
  • 150,581
  • 18
  • 184
  • 329