C++ Logo


Advanced search

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

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Tue, 23 Aug 2022 13:40:14 +0200
On Mon, Aug 22, 2022 at 10:08 PM Edward Catmur <ecatmur_at_[hidden]>

> On Mon, 22 Aug 2022 at 14:36, Sébastien Bini <sebastien.bini_at_[hidden]>
> wrote:
>> I'm in favor of the default operator to be aliased. However this is not
>> enough as if the assignment operator of each subclass is not aliased, then
>> you may still end-up making that many copies (or relocations).
>> I believe we do need to provide a way to clearly annotate the assignment
>> operator as aliased (that reloc keyword comes in). Also, it would serve for
>> assignment operators that are defaulted in the implementation file only.
> I contend that it's enough to mandate aliasing for relocatable-by-ABI
> types. Note that this doesn't affect ABI in that the callee still does the
> same amount of work; the difference is that the caller is required to
> observe that the source and target cannot alias and that therefore the
> parameter can alias the source instead of having to be a relocated
> temporary.

If I understand correctly, you suggest that aliasing be mandated only for
types that have a relocation constructor and have not opted-out of the ABI
break? I like this idea, it further avoids another keyword in the operator

But what about trivial types, which follow the rule of zero?

struct S { int _d; S& operator=(S rhs) { _d = rhs._d; return *this; } };

They will get a relocation constructor for free, even if none of their
subobjects explicitly provides a relocation constructor. Had they declared
a prvalue-assignment operator, then it would change its ABI silently. It's
okay for them to silently go from caller-destroy to callee-destroy as their
destructor is a no-op, but aliasing will also force the parameter to share
the same address as the caller's site source object, which is a silent ABI

They could still opt-out with the class attribute, but that requires manual
inspection of the code from users.

> What's still lacking though, but not a blocking issue in my opinion, is
>> that in the assignment operator implementation, users cannot elegantly call
>> a subobject's prvalue assignment operator. What if std::relocate where just
>> a mere cast, similar to std::move?
>> template <class T>
>> T std::relocate(const T& d) { return static_cast<T>(d); }
> That's spelled `auto(d)` (in C++23). But it performs decay-copy. The
> signature you've written has to perform a copy, because a reference to
> const cannot (logically) modify its referent.

Nice, I didn't know of that upcoming auto(d) feature.

> We need a "dangerous" std::relocate, leaving the source object in
> destroyed state, for use in containers: std::vector, std::optional; memory
> pools, etc.

See below.

> Then users can write: (with: `class T : B { D _d; };` )
>> T& T::operator=(T rhs)
>> {
>> B::operator=(std::relocate(rhs));
>> _d = std::relocate(rhs._d);
>> return *this;
>> }
> What would that do if D is non_null<unique_ptr<int>>? D cannot be copied,
> and there's no suitable empty state for _d to be left in after a move, so
> it must be left in a destroyed state; but it must not be double-destroyed.

Nice find... Indeed, for the relocation to happen the caller must be in the
position of preventing the destructor call on the source object. With an
std::relocate just promoting to a prvalue is not enough to trigger
relocation, as you pointed out. Even for types that are only-relocatable,
such an std::relocate could not guarantee relocation.

That also means that, in the prvalue-assignment operator, users cannot
manually forward to a subobject prvalue-assignment operator if that
subobject is relocation only. (Well, unless we use the union trick or

And yes, we do need an unsafe destructive relocation function then.

Received on 2022-08-23 11:40:27