Finally, reflection has arrived, five years after I last touched a line in c++. I wonder how long would it take the committee, if ever, to introduce destructing move.
C++26 adds destructive moves. They are called relocatable types.
There are edge cases where destructive moves are not safe and it is impossible for the compiler to know they aren't safe. C++ uses non-destructive moves when it can't prove the safety of destructive moves, even if destructive moves may in fact be safe. C++26 adds a type annotation that guarantees destructive moves are safe in cases where you can't prove they are un-safe.
The concept of relocatable types is actually a bit broader in scope than just destructive moves, but destructive moves are one of the things it enables. It is a welcome change.
> C++26 adds destructive moves. They are called relocatable types.
I thought those were removed? For example, see Herb's 2025-11/Kona trip report [0]:
> For trivial relocatability, we found a showstopper bug that the group decided could not be fixed in time for C++26, so the strong consensus was to remove this feature from C++26.
From the proposal, I see a bunch of new keywords and rules - alright given the language's heritage. But what happens if I "relocate" a variable value - would a "shell" remain or how exactly C++ is supposed to handle this:
auto value = create_value();
if (some_cond) {
consume_value(std::move(value)); // not sure whether it's move here, but I guess my point is clear
}
use_value(value);
My assumption is that this would produce a compiler error. Depending on whether or not the branch is taken, you would essentially be accessing an uninitialized value. Compilers already catch this type of case.
destructive moves are required to make moves zero cost.
Currently move semantics in C++ requires that A is left in a 'moved from, but valid state' which means that:
1. The compiler must still generate code that calls the destructor.
2. Every destructor need have to have some flag and a test in it like:
if(moved_from) // do nothing
else { free_resources(); }
(Granted, for some simple types the compiler might inline and removed redundant checks so it ends up with no extra code, but that is not guaranteed)
With destructive moves the compiler can just forget about the object completely, no need to call it, destructurs can be written as normal and only care about the invariants established in the constructor.
What should happen at ++B, and what should A be at the end? How would the compiler enforce this? I can see this being complicated.
The compiler can forget about it, but the code doesn't, so you mismatch between what is on screen and what the compiler does, which seems even more confusing.
You pretty much need lifetime tracking to make it foolproof.
A quick dirty hack would be to have a static analyzer drop B from the symbol table after it sees std::move and give a warning, but that obviously wont catch other references to it, but maybe it will catch the low hanging fruit.