Use after free (through references) is a standard problem in C++ through anything. But you can say post-move a value is dead, and use of existing references would be UB (sigh, but that isn't new).
But to address your point: I'm a muppet and didn't think about conditional move in a loop.
I had been thinking that you require that any conditional block has to end with a moved value int he same state - e.g.
if (a) {move(x)} else {}
The compiler would say both blocks must end with x being dead, so the else block would be required to call x.~X();
But yeah, that fails completely if you have
for (...i...) { if (i) move(x) }
because my clever rule means that x would have to die at the end of the first loop.
But the general problem I have with std::move currently is that there's no guaranteed death - the fact that post std::move a destructor will still run, or the object can just continue to be used in general means that your code has to be made defensive against already being logically dead - even if just the destructor.
Rust takes a conservative approach here --- you can't use x if it might have been moved along some control-flow path leading up to the current point. C++ could do that too.
> Use after free (through references) is a standard problem in C++ through anything. But you can say post-move a value is dead, and use of existing references would be UB (sigh, but that isn't new).
OK, arguably aliased-use-after-move could be treated as an existing form of UB. Sanitizers and static checkers would still need to be updated, however.
But to address your point: I'm a muppet and didn't think about conditional move in a loop.
I had been thinking that you require that any conditional block has to end with a moved value int he same state - e.g.
if (a) {move(x)} else {}
The compiler would say both blocks must end with x being dead, so the else block would be required to call x.~X();
But yeah, that fails completely if you have
for (...i...) { if (i) move(x) }
because my clever rule means that x would have to die at the end of the first loop.
But the general problem I have with std::move currently is that there's no guaranteed death - the fact that post std::move a destructor will still run, or the object can just continue to be used in general means that your code has to be made defensive against already being logically dead - even if just the destructor.