views:

1175

answers:

2

One of the cool new features of the upcoming C++ standard, C++0x, are "rvalue references." An rvalue reference is similar to an lvalue (normal) reference, except that it can be bound to a temporary value (normally, a temporary can only be bound to a const reference):

void FunctionWithLValueRef(int& a) {...}
void FunctionWithRValueRef(int&& a) {...}

int main() {
     FunctionWithLValueRef(5); // error, 5 is a temporary
     FunctionWithRValueRef(5); // okay
}

So, why did they invent a whole new type, instead of just removing the restrictions on normal references to allow them to be bound to temporaries?

+23  A: 

It would be pointless. You would change the thing in the function, and the change would be lost immediately because the thing was actually a temporary.

The reason for the new type stems from the need to be able to decide what actually is an rvalue and what not. Only then you can actually use them for the cool things they are used.

string toupper(string && s) { // for nonconst rvalues
    for(char &c : s) make_uppercase(c);
    return move(s); // move s into a returned string object
}

string toupper(string const& s) { // for the rest
    // calls the rvalue reference version, by passing 
    // an rvalue copy.
    return toupper(string(s));
}

Now, if you have some rvalue and pass it to toupper, the rvalue can directly be modified, because we know the temporary is a throw-away thing anyway, so we can aswell just change it and don't need to copy it. Also, the same observation is used for the thing called move-constructors and move-assignment. The right hand side is not copied, but its things are just stolen away and moved to *this.

If you were to say that rvalues can bind to non-const lvalue references, then you would have no way to figure out whether that references an lvalue (named object) or an rvalue (temporary) in the end.


It's probably more little know, but useful anyway, you can put lvalue or rvalue ref-qualifiers on a member function. Here is an example, which naturally extends the existing semantics of rvalue references to the implicit object parameter:

struct string {
    string& operator=(string const& other) & { /* ... */ }
};

Now, you can't anymore say

string() = "hello";

Which is confusing and is not really making sense most of the time. What the & above does is saying that the assignment operator can only be invoked on lvalues. The same can be done for rvalues, by putting &&.

Johannes Schaub - litb
+1 Wow thanks for the last part about the lvalues/rvalues only member call! I read a lot about C++0x but didn't see anything about that (I guess it's in the last draft but I didn't read all of it). Can you point me to some documentation about this feature?
Klaim
Here is a nice overview: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1821.htm . In the working paper, see 8.3.5, 9.3.1 and 13.3.1.
Johannes Schaub - litb
Thanks for this answer, this makes it much clearer. I just wasn't really understanding rvalue references very well. This makes a lot more sense than what I was thinking.
Zifre
I'd like to add that http://thbecker.net/articles/rvalue_references/section_01.html is a great "rvalue references for dummies" article.
rlbond
@rlbond: that is a really great article.
Zifre
rlbond, thanks for the nice article. another great one: http://blogs.msdn.com/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx
Johannes Schaub - litb
According to current rules, your toupper function won't compile anymore because rvalue references (the return value) cannot be initialized anymore with an lvalue expression (s). Also, returning rvalue references is almost always a bad idea because it may lead to dangling references. Example: string const
sellibitze
@sellibitze, oh you are right. I thought along the way "it's local, so it's treated as an rvalue in the return statement". But i missed that it's a reference variable - so it isn't treated as an rvalue. Thanks, i will fix it! Your second complaint is also justified. I will fix it all the way.
Johannes Schaub - litb
Daniel Earwicker
legends2k
@legends what was meant is "by passing a copy of the lvalue. That copy is an rvalue", contracted to "by passing an rvalue copy".
Johannes Schaub - litb
@litb: Aaah! Now I see it that way! Old habits die hard :)
legends2k
@litb: Also thanks for explaining about the little known ref-qualifiers!
legends2k
+4  A: 

Because adding a new kind of reference allows you to write two overloads of a method:

void CopyFrom(MyClass &&c)
{
    dataMember.swap(c);
}

void CopyFrom(const MyClass &c)
{
    dataMember.copyTheHardWay(c);
}

The version that accepts the new kind of reference is allowed to modify the variable it receives, because that variable isn't going to be used anywhere else. So it can "steal" the contents of it.

This is the whole reason this feature was added; retaining one type of reference wouldn't achieve the desired goal.

Daniel Earwicker