Hello!
I want to express my concerns regarding the syntax proposed in "Trivial Relocatability for C++" (P2786R11).
The authors of P2786 use the class property specifiers "memberwise_trivially_relocatable" and "memberwise_replacable" to signify that a class is trivially relocatable, like so:
class MyClass
memberwise_trivially_relocatable
memberwise_replacable
{
// You still need to define these!
MyClass(MyClass&&) {
/* code */
}
MyClass& operator=(MyClass&&) {
/* code */
}
};
You say "You still need to define these" — but that highlights your main misconception here. The whole point of trivial relocatability — whether it's the industry standard P1144 that WG21 doesn't talk about, or the P2786 version out of Bloomberg (which Bloomberg itself doesn't use) — is that you can add trivial relocation on top of an existing class with non-trivial move-construction, non-trivial assignment, and/or non-trivial destruction.
"Relocation" is the quantity-preserving analog of "copying." A set of trivially copyable objects can have arbitrarily many copies of their values made at arbitrary memory locations simply by putting the right bytes there. A set of trivially relocatable objects can be relocated to arbitrary memory locations simply by putting the right bytes there if and only if the mapping from source values to destination values is 1:1.
For example, `unique_ptr<int>` is non-trivially move-constructible, non-trivially move-assignable, and non-trivially destructible; yet it remains trivially relocatable, because when you combine those value-semantic operations in certain ways (such that each existing value is preserved, possibly in a new location in memory), the result is triviality (that is, "as if by copying the bytes").
So this explains why your next comment misses the target:
My primary issue with this syntax is how 'out of place' the specifiers are. They are related to the move special member functions, yet are nowhere near them.
The indication that a class is trivially relocatable pertains to the class as a whole. It doesn't pertain to any single member function. That's why it has to go on the class as a whole.
Consider the classic rule-of-five. While writing a class that follows this rule, we define the constructor, the copy constructor, the copy assignment, the move constructor, the move assignment, and the destructor. And then, we need to go back to add the specifiers (which, again, will be the case for most classes!).
Why go back? Couldn't you start by adding the specifier in a single place, and then proceed to write the special member functions afterward? :)
I propose the following replacement:
class MyClass {
MyClass(MyClass&&) [[memberwise_trivial]] {
/* code */
}
MyClass& operator=(MyClass&&) [[memberwise_trivial]] {
/* code */
}
};
The main point of this change is to move the specifiers into a location more suited to their meaning. We also won't need to differentiate between 'trivial move construct' and 'trivial move assign'. That's up to where you place these attributes.
C++ already has `is_trivially_move_constructible` and `is_trivially_move_assignable` as individual traits. Trivial relocation doesn't have to do with those. Trivial relocatability is a whole-class property, just like trivial copyability.
I recommend you read
which will be discussed by LEWG at the Hagenberg meeting at 7:30am New York time tomorrow.
It ends by linking to four blog posts that describe trivial relocation (in the industry-standard P1144 sense, not quite the P2786 sense — in fact one is extremely anti-P2786) as well as my own blog's #relocatability tag archive.
Cheers,
Arthur