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?
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, [...]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With