Date: Fri, 25 Feb 2022 02:58:56 +0400
Ok guys, i was trying to answer to the following post by kilian, then got mislead by the examples, then corrected by Jason, then adjusted my statement. Here is the original post:"Hello,
https://eel.is/c++draft/class.base.init#12
says:
In a non-delegating constructor, the
destructor for each potentially constructed subobject of class
type is potentially invoked ([class.dtor]);
Below there is also a motivating note https://eel.is/c++draft/class.base.init#note-5:
This provision ensures that destructors can be called for
fully-constructed subobjects in case an exception is thrown
([except.ctor]).
The note makes sense. But I think the paragraph #12 is too
restrict. See following example:
---
class PublicInterface {
std::unique_ptr<class Impl> pimpl_;
};
---
For the (compiler generated) default constructor of PublicInterface, class.base.init#12
requires that the destructor of pimpl_
is potentially invoked,
even if there is no possible code path in the (compiler
generated) default constructor of PublicInterface
which actually might invoke it.
I would like to propose to relax paragraph #12. I am not a
wording-expert, but in my own words I would change it to
something like:
"In a non-delegating constructor of type T, the destructor for
each potentially constructed subobject S of class type is
potentially invoked,
if there is any potentially throwing code after the
construction of S and before the end of the constructor of T."
Here are a few examples for what I am aiming at:
---
class FirstExample {
std::string a_; //destructor is
potentially invoked, because the constructor of b_ (taking a const
char*) isn't noexcept.
std::string b_ = "Hello"; //destructor is not potentially
invoked, because the constructor of c_
(default constructor) is noexcept.
std::string c_; //destructor is not
potentially invoked, because there are no further member.
};
class SecondExample {
std::string d_; //destructor is potentially
invoked, because the initializer of e_
(new int) can throw.
int* e_ = new int;
};
void ThisCanThrow();
class ThirdExample {
std::string f_;
ThirdExample() { ThisCanThrow(); } //destructor of f_ is potentially invoked, because
the body of the constructor of ThirdExample
calls a potentially throwing function.
};
---
I would very much welcome your feedback.
Best Regards
Kilian"End of original post!Nadir Sent from my Galaxy
-------- Original message --------From: Jody Hagins <coachhagins_at_[hidden]> Date: 2/25/22 2:09 AM (GMT+04:00) To: std-proposals_at_[hidden] Cc: organicoman <organicoman_at_[hidden]> Subject: Re: [std-proposals] Relax condition for potentially invoked destructor in constructor It is possible that I misunderstand your email, but unless that is the case, I think your following statement is incorrect.But data members of an object, as the proposal points to in its example, can be constructed in different order, at best you will have a compiler warning telling you that you initialized some data member before the other.11.4.19 addresses layout of data members... • [Note: Non-static data members of a (non-union) class with the same access control (11.9) and non-zero size (6.7.2) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified. Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (11.7.2) and virtual base classes (11.7.1). —end note]11.10.2.13 addresses initialization order. • In a non-delegating constructor, initialization proceeds in the following order: • First, and only for the constructor of the most derived class (6.7.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list. • — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).— Finally, the compound-statement of the constructor body is executed. On Feb 24, 2022, at 10:40 AM, organicoman via Std-Proposals <std-proposals_at_[hidden]> wrote:Doh! I meant data members.Wow the proposal examples was so misleading, i was thinking about data members construction after reading the examples then i extrapolated to base classes.Yes, as Jason pointed, base classes initialization order is well defined.But data members of an object, as the proposal points to in its example, can be constructed in different order, at best you will have a compiler warning telling you that you initialized some data member before the other.class PublicInterface {
std::unique_ptr<class Impl> pimpl_;
};"pimpl_", is data member not a base class (subobject)Thanks Jason, nice catch, it was lucid 👌 NadirSent from my Galaxy-------- Original message --------From: Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> Date: 2/24/22 6:41 PM (GMT+04:00) To: std-proposals_at_[hidden] Cc: Jason McKesson <jmckesson_at_[hidden]> Subject: Re: [std-proposals] Relax condition for potentially invoked
destructor in constructor On Thu, Feb 24, 2022 at 8:22 AM organicoman via Std-Proposals<std-proposals_at_[hidden]> wrote:> But remember that the compiler is free to rearrange the construction of subobjects for optimization purposes,Um, no it isn't. The order of initialization of subobjects iswell-defined ([class.base.init]/13: declaration order, base classesfirst) and not allowed to vary. Compilers are not free to re-orderthose subobject constructors.The OP's point therefore stands: if all of the constructors forsubobjects past a certain point are not potentially throwing, thennone of their destructors *need* to be potentially invoked.That said, I don't think it's a very *useful* idea. It makes"potentially invoked" a matter of exactly how you initialize anobject, which sounds very brittle. One constructor might considercertain destructors to be "potentially invoked" while another may not.And I'm not sure what any of this would actually purchase you in termsof useful functionality.-- Std-Proposals mailing listStd-Proposals_at_[hidden]://lists.isocpp.org/mailman/listinfo.cgi/std-proposals-- Std-Proposals mailing listStd-Proposals_at_[hidden]://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
https://eel.is/c++draft/class.base.init#12
says:
In a non-delegating constructor, the
destructor for each potentially constructed subobject of class
type is potentially invoked ([class.dtor]);
Below there is also a motivating note https://eel.is/c++draft/class.base.init#note-5:
This provision ensures that destructors can be called for
fully-constructed subobjects in case an exception is thrown
([except.ctor]).
The note makes sense. But I think the paragraph #12 is too
restrict. See following example:
---
class PublicInterface {
std::unique_ptr<class Impl> pimpl_;
};
---
For the (compiler generated) default constructor of PublicInterface, class.base.init#12
requires that the destructor of pimpl_
is potentially invoked,
even if there is no possible code path in the (compiler
generated) default constructor of PublicInterface
which actually might invoke it.
I would like to propose to relax paragraph #12. I am not a
wording-expert, but in my own words I would change it to
something like:
"In a non-delegating constructor of type T, the destructor for
each potentially constructed subobject S of class type is
potentially invoked,
if there is any potentially throwing code after the
construction of S and before the end of the constructor of T."
Here are a few examples for what I am aiming at:
---
class FirstExample {
std::string a_; //destructor is
potentially invoked, because the constructor of b_ (taking a const
char*) isn't noexcept.
std::string b_ = "Hello"; //destructor is not potentially
invoked, because the constructor of c_
(default constructor) is noexcept.
std::string c_; //destructor is not
potentially invoked, because there are no further member.
};
class SecondExample {
std::string d_; //destructor is potentially
invoked, because the initializer of e_
(new int) can throw.
int* e_ = new int;
};
void ThisCanThrow();
class ThirdExample {
std::string f_;
ThirdExample() { ThisCanThrow(); } //destructor of f_ is potentially invoked, because
the body of the constructor of ThirdExample
calls a potentially throwing function.
};
---
I would very much welcome your feedback.
Best Regards
Kilian"End of original post!Nadir Sent from my Galaxy
-------- Original message --------From: Jody Hagins <coachhagins_at_[hidden]> Date: 2/25/22 2:09 AM (GMT+04:00) To: std-proposals_at_[hidden] Cc: organicoman <organicoman_at_[hidden]> Subject: Re: [std-proposals] Relax condition for potentially invoked destructor in constructor It is possible that I misunderstand your email, but unless that is the case, I think your following statement is incorrect.But data members of an object, as the proposal points to in its example, can be constructed in different order, at best you will have a compiler warning telling you that you initialized some data member before the other.11.4.19 addresses layout of data members... • [Note: Non-static data members of a (non-union) class with the same access control (11.9) and non-zero size (6.7.2) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified. Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (11.7.2) and virtual base classes (11.7.1). —end note]11.10.2.13 addresses initialization order. • In a non-delegating constructor, initialization proceeds in the following order: • First, and only for the constructor of the most derived class (6.7.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list. • — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).— Finally, the compound-statement of the constructor body is executed. On Feb 24, 2022, at 10:40 AM, organicoman via Std-Proposals <std-proposals_at_[hidden]> wrote:Doh! I meant data members.Wow the proposal examples was so misleading, i was thinking about data members construction after reading the examples then i extrapolated to base classes.Yes, as Jason pointed, base classes initialization order is well defined.But data members of an object, as the proposal points to in its example, can be constructed in different order, at best you will have a compiler warning telling you that you initialized some data member before the other.class PublicInterface {
std::unique_ptr<class Impl> pimpl_;
};"pimpl_", is data member not a base class (subobject)Thanks Jason, nice catch, it was lucid 👌 NadirSent from my Galaxy-------- Original message --------From: Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> Date: 2/24/22 6:41 PM (GMT+04:00) To: std-proposals_at_[hidden] Cc: Jason McKesson <jmckesson_at_[hidden]> Subject: Re: [std-proposals] Relax condition for potentially invoked
destructor in constructor On Thu, Feb 24, 2022 at 8:22 AM organicoman via Std-Proposals<std-proposals_at_[hidden]> wrote:> But remember that the compiler is free to rearrange the construction of subobjects for optimization purposes,Um, no it isn't. The order of initialization of subobjects iswell-defined ([class.base.init]/13: declaration order, base classesfirst) and not allowed to vary. Compilers are not free to re-orderthose subobject constructors.The OP's point therefore stands: if all of the constructors forsubobjects past a certain point are not potentially throwing, thennone of their destructors *need* to be potentially invoked.That said, I don't think it's a very *useful* idea. It makes"potentially invoked" a matter of exactly how you initialize anobject, which sounds very brittle. One constructor might considercertain destructors to be "potentially invoked" while another may not.And I'm not sure what any of this would actually purchase you in termsof useful functionality.-- Std-Proposals mailing listStd-Proposals_at_[hidden]://lists.isocpp.org/mailman/listinfo.cgi/std-proposals-- Std-Proposals mailing listStd-Proposals_at_[hidden]://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2022-02-24 22:59:06