C++ Logo

std-proposals

Advanced search

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

From: connor horman <chorman64_at_[hidden]>
Date: Sat, 26 Feb 2022 10:58:28 -0500
On Sat, 26 Feb 2022 at 10:54, connor horman <chorman64_at_[hidden]> wrote:

>
>
> On Sat, 26 Feb 2022 at 10:43, organicoman <organicoman_at_[hidden]> wrote:
>
>> I see the problem now.
>> There is a big misunderstanding of when an object destructor is invoked,
>> and what is its relationship with the constructors.
>>
>> To be saved by heart:
>> "A destructor can be called only when the object is successfully
>> constructed"
>>
>> Exple:
>>
>> struct object
>> {
>> type1 member1;
>> type2 member2;
>> type3 member3;
>> };
>>
>> At construction time, if i construct "member1" then "member2" then "
>> member3", or
>> "member2", then "member1" then "member3",
>> Or in any other permutation possible.
>> When the constructor finishs, i will have:
>> member1, member2, member3 and the instance that contains them, all in
>> their correct places and initialized safe and sound.
>>
>> At destructor invocation time, the destructor finds all the *member
>> subobjects *in their correct places and it starts destroying from
>> whatever order the standard wants.
>>
> Yes, the order is the opposite that the objects were constructed in
> because that's what the standard specifies. However, in order to do this,
> it must know the order in which they were constructed.
> See this line in the standard:
> http://eel.is/c++draft/class.dtor#14.sentence-3
>
>
>>
>> Why? Because the destructor is only available when all the subobjects
>> exist and are in their places.
>>
>> Does it need to know how the object was constructed? NO
>>
>> How hard is this to understand?
>>
>> The only thing that matters to a destructor is that the object finished
>> construction successfully.
>>
>
> C++ defines that all objects, except for complete objects with dynamic
> storage duration (where subobjects with dynamic storage duration maintain
> this order relative to all other subobjects of the same complete object)
> with manually managed lifetimes (or when
> placement-new/pseudo-destructor expressions are involved, but same deal
> with those as with other manual lifetime management), are destroyed in the
> reverse order they are constructed.
> This allows, among other things, that one object can refer to another
> constructed earlier, and in its destructor use a pointer/reference to the
> object in a way that requires the latter to be within its lifetime. This is
> true even for member subobjects. The destructor of the class, in order to
> preserve this guarantee, therefore needs to know what the order of
> construction of base class and member subobjects was in order for it to
> destroy them in the reverse order of destruction.
>
>>
>> Sy Brand !!
>> Where are you?
>> Help!
>>
>>
>>
>>
>> Sent from my Galaxy
>>
>>
>> -------- Original message --------
>> From: connor horman via Std-Proposals <std-proposals_at_[hidden]>
>> Date: 2/26/22 7:00 PM (GMT+04:00)
>> To: std-proposals_at_[hidden]
>> Cc: connor horman <chorman64_at_[hidden]>
>> Subject: Re: [std-proposals] Relax condition for potentially invoked
>> destructor in constructor
>>
>>
>>
>> On Sat, 26 Feb 2022 at 07:05, Arvid Norberg via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> On Sat, Feb 26, 2022 at 9:28 AM organicoman via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>>
>>>> The current implementation which assumes that the member initialization
>>>> list calls the member subobjects constructors in order of declaration,
>>>> otherwise garbage is produced.
>>>> This is disaster!
>>>>
>>>
>>> It's not so much an assumption as a definition, in order to keep things
>>> simple and efficient. The "disaster" is quite easily averted by "-Wall
>>> -Werror". As I said earlier, all major compilers will tell you if your
>>> initializer list is in a different order than declaration order.
>>>
>>>
>>>> Where is the difficulty to implement it the following way:
>>>> 1- At compile time, scan the constructor member initialization list.
>>>> 2- create a dependency graph.
>>>> 3- make sure that *class subobjects *are initialized first in their
>>>> order of inheritance.
>>>> 4- generate code for the constructor under compilation, which will call
>>>> the *member* *subobjects* constructors in different order than their
>>>> declaration order in the class, as per the user wish.
>>>>
>>>> Isn't more intuitive?
>>>>
>>>
>>> You keep hand-waving away the problem of the destructor possibly being
>>> defined in a separate translation unit that cannot see the definition of
>>> the constructor. You also hand-wave away the possibility of having two
>>> constructors that end up with different initialization orders. In that case
>>> you need to record, at run-time, which constructor was used for a specific
>>> instance, so the destructor knows which order to destroy its subobjects in.
>>> Doing either is a net negative in my book.
>>>
>> Indeed. An easy case would have an inline (probably defined as default)
>> destructor, and a non-trivial out-of-line constructor? What order does the
>> defaulted destructor use to destroy the elements? Does it different between
>> translation units (ODR)? Is it the same (potentially no longer
>> reverse order of construction)? A combination of both is most likely under
>> the above proposal, so you've not only broken the guarantee of
>> Reverse-order of construction always, but you've also made the program
>> IFNDR. In order for the proposal to actually be functionally viable it
>> requires that the definition of every constructor be available in the
>> translation unit that defines the out-of-line destructor or in every
>> translation unit that contains the definition of the inline destructor,
>> *and* every constructor must use the same initialization order. And what
>> about default-initialized fields/fields with default-member-initializers?
>> What order do they get initialized in? What happens with some constructors
>> use explicit initializers for them, and others don't? The rule seems simple
>> in theory, but in C++ it's far too complex, makes far too many trade-offs,
>> and arguably comes with non-zero cost.
>>
>>>
>>> You could invent a build system that guarantees that all translation
>>> units containing constructors are compiled first, then communicate which
>>> order they ended up deciding to initialize subobjects in, and then feed
>>> this information into all the translation units that contain destructors. I
>>> hope you see the difficulty and complexity of trying to do that (for
>>> virtually zero benefit).
>>>
>>> As a program writer, i will be free to not watch in which order i
>>>> initialize my member objects, when writing my constructor.
>>>>
>>>> It is so flexible and saves the day.
>>>>
>>>
>>> Passing "-Wall -Werror" to your compiler also saves the day, and is
>>> simpler.
>>>
>>>
>>>> An object constructor is sensible piece of code, making it
>>>> non-intuitive could lead to disaster.
>>>>
>>>> Just imagine this scenario:
>>>> " some developer want to write the constructor of a defibrillator,
>>>> then by mistake he didn't respect the order of initialization of just one
>>>> member object, now his defibrillator instead of starting at ZERO volts
>>>> power, it starts at a random value.
>>>> Two consequences, either his code damages the machine, or kills the
>>>> patient"
>>>>
>>>
>>> That's why you would be required (by law most likely) to pass "-Wall
>>> -Werror" to your compiler when building medical devices.
>>>
>>> --
>>> Arvid Norberg
>>> --
>>> Std-Proposals mailing list
>>> Std-Proposals_at_[hidden]
>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>>
>>

Received on 2022-02-26 15:58:41