0

Case 1

This was based on another question of mine posted yesterday, that I accepted, although reluctantly, which can be summarized by the following code :

{
    ...
    goto Label;
    A a;
    ...
    Label:DoSomething();
}

where A is any class with a defined constructor and a default destructor. DoSomething() doesn't use a. From my understanding (I'm still struggling with C++ basic constructs) A's destructor would not be called in this situation, since the object a was not even constructed.

Note that the VS2008 compiler gives you a warning (C4533 : initialization of 'a' is skipped by 'goto Label') not an error, unless you try to define a destructor for A, when the compiler, mysteriously, changes the warning into an error (C2362 : initialization of 'a' is skipped by 'goto Label'), as if to block someone to probe into the problem, which by the way, reminded me of a quantum phenomenon in nature.

Case 2

This is the normal occurrence of a constructorthrowing an exception, in which case the destructor is not called, as discussed here and here.

Therefore

In Case1 the constructor is not invoked, but the destructor is called to destroy something that was not created, which seems odd to me. In Case2 the constructor is called, throws an exception and the destructor is not invoked, which seems reasonable to me.

I believe these two examples deserve further clarification.

Edit

In Case2 the compiler probably uses flags to avoid destroying objects which were not constructed because of a throw. Why can't those same flags be used in Case1 to identify the objects which were not constructed because of a goto ?

Community
  • 1
  • 1
Ayrosa
  • 3,385
  • 1
  • 18
  • 29
  • If you avoided goto's all would be well. "The unbridled use of the go to statement has as an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress. ... The go to statement as it stands is just too primitive, it is too much an invitation to make a mess of one's program." -- Edsger Dijkstra, 1968 – mike jones Nov 13 '11 at 11:23
  • @mikejones: Nice quote, but that's not the question here (while I technically do agree with you) – halfdan Nov 13 '11 at 11:26
  • 4
    I can't figure out what your question is. It seems you answered your own question. What don't you understand? – David Schwartz Nov 13 '11 at 11:26
  • Exceptions have nothing in common with `goto`. If exception is thrown, execution will not come to `Label` at all, stack unwinding will occur instead. – n0rd Nov 13 '11 at 11:32
  • 1
    Your mistake is that you don't think as a compiler. You know that object `a` was never constructed. But the compiler doesn't know this. It just generates a code for `a`'s initialization (which is skipped by your `goto`), and its destruction. – valdo Nov 13 '11 at 11:50
  • @valdo Are you implying that the storage for object `a` is reserved on the stack, even though the object was not constructed ? – Ayrosa Nov 13 '11 at 12:05
  • @valdo Then I think I'm beginning to understand what's going on here. The compiler would have to call the destructor in this case, just to eliminate the storage for `a` in the stack, notwithstanding the fact that the object was not constructed. Does that sound OK ? – Ayrosa Nov 13 '11 at 12:30
  • 1
    @jaayrosa: not exactly. Allocating/freeing the stack and calling constructor/destructor is not the same. The stack is usually allocated for all the function variables at-once, and the constructors/destructors are called according to the objects life scope. The main point that should be realized: generation of the code that calls construction/destructor is done at **compile-time**. The compiler has no information that you're going to skip a part of this code. – valdo Nov 13 '11 at 14:17
  • @valdo In **Case2** the compiler probably uses flags to avoid destroying objects which were not constructed because of a throw. Why can't those flags be used in **Case1** to identify the objects which were not constructed because of a `goto` ? – Ayrosa Nov 13 '11 at 14:53

2 Answers2

4

In C++, gotos across variable initializations (constructor calls) are forbidden exactly for this reason: The goto skips the constructor call. After the goto and the call to DoSomething, the variable a would go out of scope, so the destructor would be called on a, which has not been constructed because the goto skipped the constructor call.

VS2008 is lenient here and allows a violation of the standard when an auto-generated destructor, probably on a POD, has no effect.

thiton
  • 35,651
  • 4
  • 70
  • 100
  • But what is difficult to me to understand is why the compiler calls the `destructor` in the first place ? – Ayrosa Nov 13 '11 at 11:31
  • @jaayrosa: Edited the answer accordingly. – thiton Nov 13 '11 at 11:31
  • Observe that in my [original question](http://stackoverflow.com/questions/8101252/c4533-warning-why-does-goto-skip-variable-initialization), my class isn't a POD. – Ayrosa Nov 13 '11 at 11:35
  • @jaayrosa: Then my guess at the exact amount of lenience shown by VS2008 is wrong. Nevertheless, case 1 violates the standard, so the compiler is free to do anything. Including just emitting a warning when the destructor is default. – thiton Nov 13 '11 at 11:37
  • "After the goto and the call to DoSomething, the variable a would go out of scope, so the destructor would be called on a, which has not been constructed because the goto skipped the constructor call". But how can something be destroyed if it was not created ?? – Ayrosa Nov 13 '11 at 11:48
  • It can't, obviously, which is why you get the warning. – MSalters Nov 13 '11 at 11:56
  • @jaayrosa: For the destructor call, you have to distinguish between the logical and the implementation level. On the logical level, it is there to destroy the object. But at the implementation level, the destructor is just a function like any other, and the only special thing is that the compiler inserts the function call under the hood. – celtschk Nov 13 '11 at 12:20
  • @celtschk I couldn't follow your reasoning here. – Ayrosa Nov 13 '11 at 12:37
  • @jaayrosa: At the implementation level, there are no objects which could be destroyed. There are only functions operating on data. The destructor is just a special function which is called just before the data is discarded. And it's special in that the compiler automatically inserts calls to it. – celtschk Nov 13 '11 at 12:51
3

Actually case 1 is invalid C++. The compiler has to issue a diagnostic for this and is allowed to not compile that at all. If the compiler compiles it anyway, it's completely up to the compiler what to do with it. It would be sensible to not destruct the object in that case, however that requires extra machinery, and that means unneeded overhead for correct programs (the program needs to check an internal flag whether the object has been constructed). I don't think it could always be resolved at compile time whether that check is needed because that would likely be equivalent to the halting problem.

celtschk
  • 19,311
  • 3
  • 39
  • 64
  • From your answer, could I interpret that the destructor would not really be called in **Case1**. The error (or the warning) is just a convenient way for the compiler to simplify its code ? – Ayrosa Nov 13 '11 at 11:43
  • @jaayrosa: Then you interpreted the answer wrong. Since it's not valid C++, there is *no* correct behaviour of this code. So the only way to know whether the destructor is called or not is to check with the compiler. And there it depends on whether the compiler maker is willing to put in extra effort to prevent destruction of uninitialized objects (unlikely) and thinks that users not using the non-standard feature would not mind the performance hit coming with this (also unlikely). – celtschk Nov 13 '11 at 11:52
  • Then I rephrase my comment above : Is it possible that the standard was written this way just to avoid complications for compilers' writers ? – Ayrosa Nov 13 '11 at 12:01
  • 1
    @jaayrosa: It is possible. But more likely it was written this way to avoid the overhead for the non-users of this feature. After all, a basic guiding principle for the development of C++ is "you don't pay for what you don't use". – celtschk Nov 13 '11 at 12:16
  • As I said before, I'm no expert in C++, but from my side, this is the best answer I've got so far. At least it has some logic supporting it. It is not just a dogma. – Ayrosa Nov 13 '11 at 12:58