C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relax condition for potentially invoked destructor via noexcept

From: Jens Maurer <jens.maurer_at_[hidden]>
Date: Fri, 29 Sep 2023 15:11:38 +0200
On 28/09/2023 14.14, Kilian Henneberger via Std-Proposals wrote:
> last year I started a thread in this mailing list: [std-proposals] Relax condition for potentially invoked destructor in constructor <https://lists.isocpp.org/std-proposals/2022/02/index.php#msg3534>.
>
> I aimed at adjusting https://eel.is/c++draft/class.base.init#12. Right now the wording says that the destructor for each potentially constructed subobject is potentially invoked. And the wording requires this unconditionally. I wanted to add conditions, under which the destructor of each potentially constructed object is potentially invoked. Given following class:
>
> struct S {
> std::unique_ptr<class Impl> pimpl;
> S() : pimpl() {}
> };
>
> We know, that after the construction of pimpl, the constructor of Shas nothing else to do and, especially, will never throw anything. Therefore we never actually have to invoke the destructor of pimplfrom within the constructor of S. But as of now, the wording requires that the destructor of pimplis potentially invoked in the constructor of S. Therefore the code won't compile as trying to invoke pimpl's destructor requires Implto be a complete type which it isn't at that point. As you can deduce from my example, I aim at making the PImpl idiom easier to apply.

My understanding of the pimpl idiom is that you'd actually create
a value of type "Impl" in (all) the constructors of S, and the
implementation of those constructors is actually out-of-line,
as is the definition of class Impl.

Do you have other (and more complete) motivating examples?

> Last year I received valuable feedback on this mailing list.
> A) While I only mentioned https://eel.is/c++draft/class.base.init#12, Lénárd Szolnoki <https://lists.isocpp.org/std-proposals/2022/02/3539.php>pointed out that I should also take a look at https://eel.is/c++draft/stmt.return#3, which has a similar wording for similar reasons and could similarly be adjusted. There are further places in the standard, which require a destructor to be potentially invoked. For completeness I list them all here:
> https://eel.is/c++draft/class.base.init#12
> https://eel.is/c++draft/expr.new#25
> https://eel.is/c++draft/stmt.return#3
> https://eel.is/c++draft/dcl.init.aggr#9
> https://eel.is/c++draft/except.throw#5
> I think the only relevant ones are [class.base.init#12] and [stmt.return#3].
>
> B) Jason McKesson <https://lists.isocpp.org/std-proposals/2022/02/3536.php> gave the valuable feedback thatmy original idea makes "potentially invoked" a matter of exactly how you initialize an object, which sounds very brittle. And I agree. Therefore I am proposing an alternative idea: Instead of making "potentially invoked" a matter of how you initialize an object and which other subobjects might get initialized afterwards, we use noexceptas measure to decide whether destructors are potentially invoked within a constructor. So if a constructor is marked noexcept, we know that there is no way for an exception to be thrown out of that constructor. Therefore, we also have no need for the destructors of potentially constructed subobjects to be invoked. Addressing https://eel.is/c++draft/stmt.return#3would work in a similar way: The destructor for the result object of a function should only be potentially invoked if the function is not marked noexcept.

Even if the function is "noexcept", I think you can still be in a situation
where you need to invoke the destructor of the return object prior to hitting
the "noexcept" on the function, e.g. when temporaries are involved.

Jens

Received on 2023-09-29 13:11:42