2

We can't bind non-const lvalue reference to an rvalue, but it can be bound to the const one. We can't bind rvalue reference to an lvalue also. Actually the Standard say so:

8.5.3/5.2:

the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

But is there a better explanation for the things than "The Standard say so"?

2 Answers2

3

Because it doesn't make semantic sense.

You can't bind a non-const lvalue reference to an rvalue, because what does it mean to modify an rvalue? By definition nothing else will see the result, so this doesn't make sense.

int& i = 3;
//should change referenced location, but we aren't referencing a memory location
i = 5; 

You can't bind an rvalue reference to an lvalue because rvalue references exist to facilitate destructive optimizations on their referencee. You wouldn't want your objects to be arbitrarily moved out from under you, so the standard doesn't allow it.

void process_string (std::string&&);
std::string foo = "foo";
//foo could be made garbage without us knowing about it
process_string (foo); 
//this is fine
process_string (std::move(foo));
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Couldn't you explain in a nutshell what destructive optimization is.... haven't faced with that concept. –  Jul 02 '15 at 14:37
  • 2
    Have a look at [move semantics](http://stackoverflow.com/questions/3106110/what-are-move-semantics). Explaining them is a bit out-of-scope for this question, but that'll help you understand what I mean. – TartanLlama Jul 02 '15 at 14:40
  • Short version is: a move operation, unlike a copy operation, is allowed to do things that leave its source object in a "wrecked" state, which can only be destroyed. For example, if you have an object which internally has a pointer to some private data, then to copy the object you must make a copy of that data. But to move it, the move destination can simply take over the pointer to storage, and leave a `nullptr` in the source object. Further operations on the source are undefined (the other members may make no sense at this point), but the destructor should still clean it up right. – Mike DeSimone Jul 02 '15 at 14:57
3

Think to some real cases:

#include <vector>

void f1( int& i ){i = 1;}
void f2( const int& i ){i = 1;}
void f3( std::vector<int>&& v ){auto v_move{v};}

int main()
{
    f1(3); // error: how can you set the value of "3" to "1"?
    f2(3); // ok, the compiler extend the life of the rvalue "into" f2
    std::vector<int> v{10};
    f3(v); // error: an innocent looking call to f3 would let your v very different from what you would imagine
    f3(std::vector<int>{10}); // ok, nobody cares if the rvalue passed as an argument get modified
}
Paolo M
  • 12,403
  • 6
  • 52
  • 73