C++ Logo


Advanced search

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

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Tue, 31 May 2022 10:51:34 +0200
Hi all,

> The defaulted operator reloc can't work here anyway, since Transaction
needs to be informed that DB has relocated.
> So Transaction must already have a ctor Transaction(Transaction&&, DB&)
and the relocator of T must invoke that ctor:
> T::operator reloc(T&& src) : _trans(std::move(src._trans), _db) {}

Indeed, my bad. I do have a gut feeling that synthesized relocation may
lead to problems, mainly because it may change the destruction order of
subobjects. However I cannot find a convincing example where the default
move constructor + destructor on the whole object does the right thing, and
where the default operator reloc() mixing true relocations and synthesized
relocations on subobjects does not :/ I guess we'll keep synthesized
relocation for the moment.

> template<class T> requires is_noexcept_relocatable_v<T>
> void swap(T& lhs, T& rhs) {
> T temp = relocate(&lhs);
> relocate_at(&rhs, &lhs);
> new (&rhs) T(reloc temp);
> }

Actually I am not sure there are any real benefits of having `T
std::relocate(T*)` if we have the reloc operator (which is a safer version
of std::relocate) and std::relocate_at. swap can be written without

template<class T> requires is_noexcept_relocatable_v<T>
void swap(T& lhs, T& rhs)
    std::aligned_storage_t<sizeof(T), alignof(T)> tmp;
    std::relocate_at(&lhs, reinterpret_cast<T*>(&tmp));
    std::relocate_at(&rhs, &lhs);
    std::relocate_at(reinterpret_cast<T*>(&tmp), &rhs);

BTW 'new (&rhs) T(reloc temp);' can be replaced by placement-reloc: 'reloc
(&rhs) temp;'

> gsl::non_null<int*> p1, p2, p3 = ...;
> gsl::non_null<int*> q1 = reloc p1; // OK in your world
> q1 = reloc p2; // OK??
> std::swap(q1, p3); // OK??

`reloc obj` (with obj of type T) merely returns a temporary object of type
T, built by relocating from obj (just like std::relocate). obj is
considered destructed and as such is not left in a moved-from state. We
rely on mechanisms such as copy-elision or clearly identified patterns
(object initialisation with a reloc statement, like: `auto a = reloc obj;`)
to remove that temporary in most cases.

With `q1 = reloc p2;`, as it is phrased in the paper, reloc p2 will return
a temporary that is assigned into q1. p2 is considered destructed
(relocated into the temporary). Then q1's move assignment operator will be

We could go further and claim that assigning from a reloc statement
transparently calls destructor + placement reloc (std::destroy_at(&q1);
reloc (&q1) p2;). I am not sure that's desirable at all:
- We don't enforce this at the language-level for other assignment
- Why limit to the operator reloc? Assigning from any pr-value of the same
type could be optimized into destructor + placement reloc, but that may
break existing code?
- How to handle exceptions? This optimization could be turned off if the
destructor or operator reloc are noexcept(false).
- Users can implement their assignment operator with std::swap which takes
advantage of relocation (as it is often the case anyway).

> Similarly, if you "relocated" a `std::mutex`, *bad things would happen*.
You can't have a mutex suddenly change memory addresses and expect the rest
of the program to just be cool with that.

Yes there are objects that are not even relocatable, like std::mutex. Such
types can prohibit relocation by deleting their operator reloc: `operator
reloc(T&&) = delete;` (which should already be implicitly deleted since
their move constructor is also deleted.)

Best regards,

On Tue, May 31, 2022 at 12:15 AM Edward Catmur <ecatmur_at_[hidden]>

> On Mon, 30 May 2022 at 12:07, Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> wrote:
>> On Mon, May 30, 2022 at 1:56 PM Edward Catmur <ecatmur_at_[hidden]>
>> wrote:
>>> [...]
>> template<class T> requires std::is_noexcept_relocatable_v<T>
>>> void swap(T& lhs, T& rhs) {
>>> T temp = relocate_at(&lhs);
>>> new (&lhs) T(relocate_at(&rhs));
>>> new (&rhs) T(relocate_at(&temp));
>>> }
>>> (relocate_at is the magic library function that (destroys and) relocates
>>> its argument into a returned prvalue.)
>> Terminology nit: `relocate_at` takes a pointer parameter `dest`, just
>> like `construct_at` and `destroy_at`.
>> *`relocate` with no suffix* is the magic library function that relocates
>> into a returned prvalue.
>> https://quuxplusone.github.io/blog/2022/05/18/std-relocate/
> Ah, yeah, thanks. And that last relocate is incorrect; it should use the
> relocation operator keyword, since temp should not be destroyed at the end
> of scope:
> template<class T> requires is_noexcept_relocatable_v<T>
> void swap(T& lhs, T& rhs) {
> T temp = relocate(&lhs);
> relocate_at(&rhs, &lhs);
> new (&rhs) T(reloc temp);
> }

Received on 2022-05-31 08:51:46