C++ Logo

std-proposals

Advanced search

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

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Wed, 17 Aug 2022 18:40:59 +0200
My bad, I didn't know that function-try-block even existed in the language.
I admit I never saw it in production code :/ Thank you for clarifying that
point.

Having that in mind, adding extra initializers between "try" and ':' seems
like a viable solution.

Hm, but you can write:
>
> T& operator=(T rhs) { std::destroy_at(this); return *new (this) T(reloc
> rhs); }
>
> If we permit operator=(T), as a special member function, the same aliasing
> power as the relocating constructor, then it invokes precisely one
> destructor call and one relocating constructor call, which is perfect. If
> it doesn't have aliasing on its prvalue rhs parameter (admittedly that
> might be tricky from an ABI standpoint), then there's potentially two calls
> to the relocating constructor, but they can be elided together.
>

Indeed, but for that to hold we do need to have a destroy-and-relocate
implementation behind the scene right? copy-and-swap, even with the
aliasing, costs at least two relocations + one destruction:

In: `T x, y; y = reloc x;`

A possible swap-based implementation would look like (considering the
reloc-assignment is inlined):

   alignas(T) std::byte buff[sizeof(T)];
   std::relocate_at(&y, &buff);
   std::relocate_at(&x, &y);
   std::relocate_at(&buff, &x);
   std::destroy_at(&x); // destroy previous `y` content

Note that the last std::relocate_at call could be alleviated, as 'x' cannot
to be reused:

   alignas(T) std::byte buff[sizeof(T)];
   std::relocate_at(&y, &buff);
   std::relocate_at(&x, &y);
   std::destroy_at(reinterpret_cast<T*>(&buff)); // destroy previous `y`
content

Having written that, I don't understand how this is better than:

   std::destroy_at(&y);
   std::relocate_at(&x, &y);


> What feels unsafe about destroy-and-relocate is that we destruct an object
>>> within a member function. We can tweak things around and turn the
>>> reloc-assignment operator into a static function behind the scene:
>>> T& operator=(T src) = reloc; // same as: static T& assign_reloc(T& dst,
>>> T& src) { std::destroy_at(&dst); return *std::relocate_at(&src, &dst); }
>>>
>>> Here we no longer have the scary std::destroy_at(this), and we avoid
>>> self-destruction in a member function (which may be an UB?).
>>>
>>
>> That's still destroying an object from within its member function, just
>> not directly. It's fine, though; `delete this` has always been legal, it's
>> just never been a sign of good code. One library I use regularly
>> (rapidjson) uses destroy-and-rebuild for (lvalue) assignment, and it works
>> fine - as long as you sort out exception safety.
>>
>
How am I destroying from a member function?

Is the reloc-assignment such a blocking point for this proposal? Can't we
leave it out at first, knowing that users can still easily write their own
(albeit it won't be defaulted nor aliased)? Can't a second proposal solve
this specific point once we have more user experience as you mentioned?

Regards,
Sébastien

Received on 2022-08-17 16:41:12