From what I\'ve read and seen you cannot bind an expression that is an rvalue to an lvalue reference. What I have seen however is that you can bind an rvalue to an rvalue refere
When you call foo(1)
, a temporary int
equal to 1
is created, and int&& a
is bound to it. You can freely change it. The same way you can write int&& a = 1;
. a
itself is an lvalue, so you can bind lvalue reference to it.
You may read a great article Universal References by Scott Meyers which explains rvalue and lvalue references in detail.
It's a fundamental rule of C++ and it prevents bugs:
int foo();
int& x = 3; // whoops
int& y = foo(); // whoops (sometimes)
"Rvalue references" (a set of types; not to be confused with actual rvalues) were created at least in part so that you can still do this if you really want to:
int&& x = 3; // oh, go on then *sigh*
int&& y = foo(); // you'd better be sure!
In the previous examples, I bind (or attempt to bind) objects "referenced" by an rvalue expression to a reference.
Now, I shall bind the object named by an lvalue expression to a reference:
int i = foo();
int& x = i; // no problem michael
And to make sure that you really meant to obtain an rvalue reference from an lvalue expression, introducing the incredibly poorly-named std::move
:
int&& x = std::move(i); // doesn't move anything
These later rules came much, much later than the original, fundamental rule that has doubtless prevented many bugs over the past twenty years.
Note that Visual Studio has historically accepted T& x = bar()
where T
is a user-defined type; go figure.
What is the reason behind disallowing binding an rvalue to an lvalue reference?
No answer to this question can be complete without a reference to the invaluable and distinguished source, The Design and Evolution of C++ by Bjarne Stroustrup.
In section 3.7 Bjarne writes:
I made one serious mistake, though, by allowing a non-
const
reference to be initialized by a non-lvalue. For example:void incr(int& rr) { rr++; } void g() { double ss = 1; incr(ss); // note: double passed, int expected // (fixed: error in Release 2.0) }
Because of the difference in type the
int&
cannot refer to thedouble
passed so a temporary was generated to hold anint
initialized byss
's value. Thusincr()
modified the temporary, and the result wasn't reflected back to the calling function.
I highly recommend The Design and Evolution of C++ for understanding many of the "why questions" one might have, especially regarding the rules that were laid down prior to the C++98 standard. It is an informative and fascinating history of the language.