In any kind of correct code, the difference between a move and a copy is only performance. If a copy were to happen where a move was requested then the code is just as correct, so I find it strange to get so hung up on it not being “real”.
Also, if move is the only available option, and move cant happen, you get a compiler error. If performance is correctness for a type that is expensive to copy, make copy not an option.
Also, there is a move constructor by default so it’s not opt-in, you opt out only if you start screwing around with copy / assignment / destructors which you usually shouldn’t need in modern code anyway.
Sure, the state of moving on the moved from object is unspecified, but really, I can’t think of a time when I’ve written code that would care. It’s kind of a non-problem.
If you really want to reuse an object after a move I question your motives, but you should just reinitilaise it by assigning a freshly constructed value and the result of that is of course standard.
That's just not true when you take smart pointers into account. unique_ptr is pretty obvious, since it can't be copied. But shared_ptr is more devious, as there is a clear semantic difference between giving someone a copy of your shared_ptr vs moving your copy to them. And, given that destructors are often used for more than simple resource cleanup (e.g. they are sometimes used for releasing locks), the difference between a move and a copy can have a huge impact on program behavior.