13

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";
}
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421

1 Answers1

5

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.

Community
  • 1
  • 1
  • @Barry The first `break;` is never executed, since `i` has a value of `2`. The question is whether the *second* `break;` should destruct `st`, even though it was not initialised and not used in the `case 2` statements. Without special wording to prevent it, it would be destructed, but destructing it may not be the right thing to do. Hence the prohibition of jumping to `case 2`. –  Oct 01 '16 at 13:22
  • Ah, yes. Should add all of that to the answer. – Barry Oct 01 '16 at 13:23
  • Thanks! Reading between the lines, the paper seems to be saying that the destructor should run if the trivial constructor was notionally run. To me, if the object can be used within the scope, then it is effectively constructed, and the destructor should certainly be run. Hmmm… – Potatoswatter Oct 01 '16 at 13:23
  • @Barry Thanks for the suggestion. I hope it's clearer like this. –  Oct 01 '16 at 13:25
  • @Potatoswatter I agree that's a very reasonable point of view, but I don't think it's the only reasonable one. :) –  Oct 01 '16 at 13:27
  • @hvd Yep, that's the part that I missed originally (even though the code example is perfect). +1. – Barry Oct 01 '16 at 13:29
  • @hvd Nitpick, but `break`s don't cause destructors, the end of a scope causes destructors, so it's the `}` and not the `break`. – Andre Kostur Oct 01 '16 at 15:19
  • @AndreKostur I don't think that's right. Sure, it's the exiting of the block that would cause `st` to be destructed, but it's the `break;` statement that would cause the block to be exited here, not the simple fact that execution reaches the closing `}`. –  Oct 01 '16 at 15:34