Order of declaration (order of construction/reverse order of destruction) is not the same thing as the order of the memory layout.

On Fri, Feb 25, 2022 at 9:30 AM organicoman via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

 Keep in mind that objects are guaranteed to be destroyed in the opposite order of construction. If you have multiple constructors that may construct your subobjects in different orders, you must also allocate run-time memory to record the order of initialization in order for your destructor to destroy the objects in the correct order. That's an unacceptable cost to a lot of people.

What you are talking about here, and i guess most of the participant in this discussion,  is the base classes of a derived class (known as subobjects).
For that case. Yes, the order of destruction MUST follow the order of construction in reverse, and that is guaranteed by the standard.

In our case we are talking about " MEMBER Objects", objects declared inside the body of the structure.
The compiler is free to rearrange their layout however it sees fit for a compact memory and to avoid extra instruction to fetch a member if it is not at the right memory boundary. 
Imagine this situation:
struct object
{
    char c;
    int i;
    short s;
};

Obviously the compiler can, and maybe must, move the char member object, to the end of the list of members and add some padding to make the memory compact and at 4's. Making the order of initialization irrelevant. 

Again my subject is order of initialization of data members , 
Look at it this way:
1- compiler allocates memory equal to the size of all data members plus base classes.
2- compiler construct base classes and initialize them respecting the order of inheritance as pointed by the standard (left to right...etc)
3- at member initialization,  the compiler construct in place the members. In the chunk of memory allocated for them. Using their indices offset and consuming their respective amount of memory. 

Obviously the order is irrelevant for the 3rd step.
Since the index of the memory location where to construct the data member is known, and the data members doesn't rely on each other to exist!
The memory layout is not dynamically changing because of initialization of the members. Right!

And without even considering the above. A struct is not tightly related to its members declaration order.
i.e:
struct a { int i; double d;};

is not different than

struct b { double d; int i;};

Memory layout and  member access wise.

 If i want to access any member i just need to invoke its name, and the compiler will translate it into a pointer difference from the beginning of the struct location.
ptr_mem = ptr_struct + ptr_diff

So if my object is successfully constructed,  and i want to destroy it, i don't care if i destroy "i" first or "d" first. 
Data members existance is not relying on each others in any possible way. Nor the object which contains them is relying on their memory layout except for access purpose.

Again "class subobjects <> class member objects"

A class subobject is a member object , but a member object  is not a class subobject.

So, i think we need to make the order of initialization of data members,  irrelevant. And separate it from the subobjects initialization. 
And keep all the rest.

What do you think?

Nadir


Sent from my Galaxy


-------- Original message --------
From: Arvid Norberg <arvid.norberg@gmail.com>
Date: 2/25/22 8:14 PM (GMT+04:00)
To: std-proposals@lists.isocpp.org
Cc: organicoman <organicoman@yahoo.fr>
Subject: Re: [std-proposals] Relax condition for potentially invoked destructor in constructor

On Fri, Feb 25, 2022 at 9:41 AM organicoman via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
[...]

So calling "test()" construtor, in my logic, will do the following:
- i want to initialize "b", but what does "f" refer to?
- ok let's find "f" and initialize it,
- good found "f", but what "a" refers to?
-look for "a",
-ok "a" is an 'int', initialized to be equal to 22.
- let's back track,
 - "f", inited
 - "b" , inited
- "test" constructed 
Now in case any of the members throw an exception,  then all the members data that were fully constructed are allowed to call their destructor. Exactly as per the standard "potentially constructed -> potentially call destructor"

That was my mental model, and that was how i would implement it.

Keep in mind that objects are guaranteed to be destroyed in the opposite order of construction. If you have multiple constructors that may construct your subobjects in different orders, you must also allocate run-time memory to record the order of initialization in order for your destructor to destroy the objects in the correct order. That's an unacceptable cost to a lot of people.

When you think about it, even allowing you to define a construction order different from the declaration order causes a similar problem. in case the destructor is implicitly defined, in a translation unit where it may not have access to the constructor's initialization order.

--
Arvid Norberg
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals