C++ Logo

std-proposals

Advanced search

[std-proposals] Relocation in C++

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Tue, 1 Feb 2022 10:26:15 +0100
Hello everyone,

I've worked on a proposal in the last months to introduce relocation (a
destructive move) in C++. I wrote the proposal (you will find it enclosed
with this email) but it's quite large so I'll write a small overview here.

Relocation allows to "move" one object into another one. It is similar to
move constructors, except that relocation guarantees that moved objects are
destructed right after and cannot be reused.

This proposal is motivated by:
- the confusing and often not properly implemented "moved-from" state,
introduced by move constructor.
- relocation is simpler to implement than move semantics as the relocated
object can be left in a dirty invalid state.
- const variables cannot be moved with C++ move semantics. But they can
"relocated" with this proposal.

This introduces three main additions to the language:

1. Relocation reference

A new type of reference, called "relocation reference". A relocation
reference on T is denoted by T~. This reference is mainly introduced
because of the new constructor.

2. Relocation constructor

We introduce a new constructor, the relocation constructor:
class T {
  T(T~ other) noexcept;
};

This constructor has a new feature no other C++ constructor has: *it acts
as a destructor with regards to its parameter* ("other" in the code
sample). Hence when this constructor is called, a new instance is built as
always, but the instance it was built from is destructed. This means that
the destructor of the moved instance must not be called (otherwise the
instance would be destructed twice).

This constructor is usually quite straightforward to implement, you need
simply to copy all data-members from other into the new instance.

For instance, implementing the relocation constructor for std::unique_ptr
is simple. You simply need to copy the internal pointer to the new
instance. *The moved instance can be left untouched, and the memory it
still owns won't be deleted as the moved instance destructor will not be
called*.
In fact there is so little to do that the default implementation does the
job:
unique_ptr(unique_ptr~) noexcept = default;

3. The reloc operator

Lastly, we introduce a new unary operator: reloc. It will usually be used
like this: auto y = reloc x;
This relocates x into y, leaving x in a destructed state.
This new operator (a) handles the construction of the new instance (will
use the relocation constructor, the move constructor or the copy
constructor, picked in that order), (b) ensures the destruction of the
relocated instance and (c) prevents any further use in the code of the
relocated instance.

Consider the following scenario:
const T x;
auto y = reloc x;
// std::cout << x << std::endl;

The second line builds y from x:
- Case 1: the type of x provides a relocation constructor: The relocation
constructor is called. At the end of the expression x is considered
destructed because of the relocation constructor. The destructor of x will
not be called when its end of scope is reached.
- Case 2: the type of x does not provide a relocation constructor, but a
move or copy constructor: The move constructor is called if it exists (if
not the copy constructor is called) to construct y from x. reloc must
ensure the destruction of x, so the destructor of x is called at the end of
the evaluation of the second line.

The third line attempts to reuse a variable that was relocated.
Uncommenting this line will raise a compile error. reloc forbids further
mention of a name that resolves to a relocated object.

This is it, thank you for reading :)

Best regards,
Sébastien Bini

Received on 2022-02-01 09:26:30