C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relocation in C++

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Fri, 20 May 2022 07:01:58 -0600
On Tue, 17 May 2022 at 21:27, Thiago Macieira <thiago_at_[hidden]> wrote:

> As you said:
>
> > Yes, you're correct. However note that if we add one more data member z,
> we
> > can end up in this situation: x successfully relocates, y throws, but to
> > release the resource T needs x and z.
>
> This is improperly-coded and is the developer's fault.
>

Really? It seems to me that any class that owns a resource and has
non-trivial members has this problem in some form. Sure, you could say that
this is a layer violation, but sometimes you do want to layer resources -
not all resources are new-delete memory.


> > I would say that if a class has a user-defined destructor (and so
> > is suspected of owning resources directly), and if at least one of its
> > bases-and-members has throwing relocate, then its relocator cannot be
> > defaulted (declared or defined as defaulted) but must be user-defined (or
> > deleted). Also, a user-defined relocator (i.e. one not declared as
> > defaulted or deleted) should not be noexcept if unspecified; either they
> > should be noexcept(false) if unspecified, or a noexcept specification
> > should be required.
>
> You'll never get a "suspected of having" in the standard. The choice needs
> to
> be by the signature of the methods involved. If those are synthesised,
> then
> the signature is obtained from the members of that class and its base
> class(es), but that does not change the rule.
>

We already default special member functions according to the presence of
other special member functions on the same class; constructors suppress the
default constructor, the copy constructor suppresses the move constructor,
and I'm fairly sure the destructor suppresses something though I can never
remember what. So it should be fine for the presence of a user-defined
destructor to have some effect on the relocator.

We have a precedent for noexcept(true)-by-default and that's the
> destructor. I
> think it could be acceptable to make the relocator obey the same rule
> because
> after it's run -- and whether it has thrown or not -- one object's
> lifetime
> has ended.
>

The justification for making destructors noexcept by default is to be able
to invoke destructors during stack unwinding, since that is a process that
cannot be allowed to fail. There is no such requirement on relocation, so I
really don't see why allowing it to fail could cause any problems.

Also, while relocation does end the lifetime of an object (as a region of
storage), the *value* that was located in that object continues to exist,
dematerialized to a prvalue (and possibly subsequently rematerialized to
another lvalue), so I don't think relocation is all that similar to
destruction.


> We also have the choice here whether such synthesised relocator is
> permissible
> in the first place if either the move constructor or the destructor are
> noexcept(false). That is, the rule would be that it's deleted by default
> and
> if one wants to have a relocator in such a class, they'll have to
> explicitly
> declare one. We can always relax this rule later.f
>

We want aggregate-style classes (without user-defined special member
functions) to Just Work. Deleting the relocator by default would not only
be inefficient, it would be problematic if any member is relocate-only (has
a relocator but no copy or move constructor). It would be incongruous for
users to have to explicitly default a relocator when all the other special
member functions are implicitly defaulted.

That's not a deal-breaker, but it's the next best thing. If adding this
> member
> changes the ABI, then it won't be added for a lot of code that needs to
> retain
> compatibility, including std::unique_ptr.
>

And if we don't get the ability to pass smart pointers in registers, and to
pass around relocate-only types and use them without gymnastics, then
there's really no point in this proposal at all. If some platforms are
scared of breaking ABI, so be it: they can leave std::unique_ptr without
trivial relocation and document the conformance defect; users who are
performance-sensitive will switch to a work-alike unique_ptr from Boost,
Abseil or whoever that does have trivial relocation, or write their own. We
broke ABI for std::string ten years ago; have we really lost our courage
since then? Especially since now we have more tools - inline namespaces,
modules, sanitizers - to make an ABI break less painful.

Received on 2022-05-20 13:02:11