Excellent explanation. Too bad it's necessary. && and std::move are the biggest warts in C++ (that's saying a lot), representing the need for programmers to be constantly aware of the less-than-obvious ways that a compiler might put an expression in one category or another. It's the programmer helping the compiler, instead of the other way around as it should be. In general, if I need an rvalue and it's legal to conv…
How do you even tell that it's legal to stick std::move around a variable? Here's a really simple example.
void test() { std::string s = ...; foo(s); bar(s); }
Can I change the last line to bar(std::move(s))? I can write a well-formed program that will misbehave, perhaps something like
char* global; void foo(const std::string& s) { global = s.data(); }
void bar(const std::string& s) { assert(global == s.data()); }
(Note that s.data() is not necessarily unchanged after std::move'ing the object, due to small-string optimization.)
This is contrived, but my point is that this is really tricky to do automatically.
Note also that when adding rvalue references the language committee had the additional constraint of not breaking existing programs. This makes things like automatic inference of temporariness even trickier.
The compiler does many other kinds of inference and automatic promotion/conversion, some of them far more difficult (and dangerous).
The C++ compiler does exceptionally few transformations that can change the behavior of a well-formed program. In fact, the only one that comes to mind is copy elision (aka RVO).