std::move isn't a function, it's a function template. First, it gets instantiated with T = string&, which makes its signature string&& move(string&) due to reference collapse rules. Only after instantiation, it will attempt to bind your argument (lvalue of type string) to the parameter (of type lvalue reference to string), which will, of course, succeed.
PS: for a simple demo, try to set T yourself: move<string>(a) and move<string&&>(a) will both fail to compile
Simply said, the reference collaspe is that when a reference to areference is formed, it's a lvalue refrence if any of the references applied to the type is an lvalue reference, and an r-value reference otherwise.
If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
(where P is the function template parameter type and A is the type of the argument it's called with)
which was introduced specifically to handle this case (in the proposal I linked)
Excuse me, but how is that in any way bad? It adds simplicity, not removes it! Without that, the move() function would be much more complex, and there could be no perfect forwarding!
C++ (new or old) has always been strongly biased in favour of programmers who are willing to think for themselves.
Whether that is good or bad is a matter of opinion.
There are many other programming languages which do not exhibit such a strong bias; they could be more appropriate for a whole lot of programmers.
> I think that the term "simplicity" is totally unknown to the creators of the language.
No. Just that they realize that an overriding design goal of making easy things trivial, would result in making difficult things impossible. (IMHO of course).
There are many other programming languages which do not exhibit such a strong bias; they could be more appropriate for a whole lot of programmers.
C++ is strongly biased in favour of programmers who are willing to spend their time learning complex language rules, then learning even more complex exceptions to them (for handling special cases) and then exceptions to the exceptions (to patch problems with exceptions introduced with the prev standard), instead of spending their time on simply crunching the problem they have to solve.
No. Just that they realize that an overriding design goal of making easy things trivial, would result in making difficult things impossible. (IMHO of course).
C++ doesn't make possible any more things than other, simpler languages. Especially when we are talking about solving really hard to code problems.
Without that, the move() function would be much more complex, and there could be no perfect forwarding!
There is a dozen or more languages out there that support perfect forwarding, without need of such weird constructs like reference collapsing or inferring T& by default where T was given.
reference collapsing is a trivial and obvious property of any language with reified references. If the language doesn't have it, it is defective (as C++98 was, although the defect report was accepted long ago).
Using T& in deduction when lvalue argument comes across a T&& parameter (I don't understand your "T was given" part) is a logical extension of the same rule and it's very *good* because it fits forwarding right in without any need for additional complexity.