C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relax condition for potentially invoked destructor in constructor

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Sat, 26 Feb 2022 10:34:11 -0500
On Sat, Feb 26, 2022 at 3:27 AM organicoman <organicoman_at_[hidden]> wrote:
>
> Now to Jason,
>
> Your ultimate point, that each constructor currently already needs to
> keep track of which objects have been constructed, is wrong. Because
> each constructor initializes subobjects in the same order, any
> particular constructor only has to keep track of how many subobjects
> have been initialized before the exception. When the exception
> happens, it can simply jump to the destruction code for destroying all
> prior objects. Because they all share the same construction ordering,
> they all share the same destruction ordering too.
>
> That was not my ultimate point,
> I used the idea of "keep track", to explain to others that in certain way we already know from where to start destroying our object under construction.
>
> What you want would require each constructor to have its own set of
> destruction orderings to match their separate construction ordering.
>
> No! I'm not saying that.
>
> Plus, you turn a very simple rule (subobjects are initialized in
> declaration order) into a complex one (subobjects are initialized
> maybe in declaration order and maybe not, depending on the
> constructor).
>
> Yes it is a simple idea, and its implementation is lazy.
> First of all, let me explain what i mean by keep track.
> 1- we should understand that member initialization list i.e subobject members construction, is just a list of calls to constructors.
> 2- constructors are simply functions, most of them are inlined, few of them are actual function calls.
> 3- calling these functions (i.e member constructors) in the context of constructing the object, is creating a stack frame.
> 4- raising an exception during this process, is just a matter of unwinding that stack frame. call it back tracking.
>
> So physically we are using the "Stack Segment" to keep track of what was successfully constructed in the list of subobject members, right! LIFO

That's not how constructors work. No "stack segment" is used to keep
track of anything. It's just a sequence of opcodes in assembly. If an
exception is caught in a particular place, it jumps to the routine
that destroys the subobjects. Exactly where it jumps to depends on
where it caught the exception.

Given a type with members X, Y, and Z, the way it works is like this:

```
//Constructor 1:
X()
if(exception) return;
Y()
if(exception) goto Y_fail;
Z()
if(exception) goto Z_fail;
Constructor body
if(exception) goto Body_fail;

//Constructor 2:
X(...)
if(exception) return;
Y(...)
if(exception) goto Y_fail;
Z(...)
if(exception) goto Z_fail;
Constructor body
if(exception) goto Body_fail;

//Destructor
Destructor body
Body_fail:
~Z()
Z_fail: //Z was never constructed, so we don't need to destroy it.
~Y()
Y_fail:
~X()
return
```

Notice how both constructors are jumping to the *same set* of
destruction functionality. This works because they initialize members
in the same order.

If they initialized members in a different order, they would *have to*
destroy them in the reverse of that order. If you initialize X first
then Z, and Z throws an exception, you can't just jump to `Z_fail`
because you never initialized Y. So each constructor would
have to have its own destruction order in addition to the destructor's
destruction order.

Received on 2022-02-26 15:34:20