goto
or switch
can jump over a declaration-statement given that it has no initializer and the construction is trivial — and that the object is also trivially destructible.
What's the rationale for the constraint on the destructor?
struct trivial {
trivial() = default;
~ trivial() = default;
};
struct semi_trivial {
semi_trivial() = default;
~ semi_trivial() noexcept { do_something(); }
};
void foo() {
goto good_label; // OK
trivial foo;
good_label:
goto bad_label; // Error: this goto statement
semi_trivial bar; // cannot jump over this declaration.
bad_label:
std::cout << "hi\n";
}
The current wording is a result of N2762. The paper gives the following rationale:
6.7 stmt.dcl:
Jumping over the definition of an automatic variable will pose the problem of whether the destructor for that variable should be run at the end of the block. Thus, the destructor needs to be trivial, i.e. have no effect. Similarly, the default constructor (the one potentially used to initialize the object) is also required to not do anything, i.e. be trivial. No other requirements are necessary.
I think the case to keep in mind is:
int i = 2;
switch (i) {
case 1:
semi_trivial st;
do_something(st);
break;
case 2:
break; // should st be destructed here?
}
And indeed, this is not an easy question to answer. Calling the destructor there would not be the obviously right thing to do. There's no good way of telling whether it should be called. The st
variable here is only used in the case 1
statements, and programmers would be surprised if its destructor got called by the case 2
's break
statement even though it was completely unused there and not constructed.