Search code examples
c++visual-c++c++11rvalue-reference

When, where, and why should we use "BigObject&& rv = std::move(big_obj);"?


My compiler is the latest VC++ 2013 preview.

#include <utility>

struct BigObject { ... };

void f(BigObject&&){}
void f(BigObject&) {}
void f(BigObject)  {}

int main()
{
    BigObject big_obj;

    BigObject&  r1 = big_obj; // OK.
    BigObject&& r2 = big_obj; // error C2440
    BigObject&& r3 = std::move(big_obj); // OK.
    BigObject&& r4 = r3; // error C2440

    f(r3); // error C2668: 'f' : ambiguous call to overloaded function
}

When, where, and why should we use BigObject&& rv = std::move(big_obj);?


Solution

  • When, where, and why should we use BigObject&& rv = std::move(big_obj);?

    Short answer: never, nowhere, and for the following reason:

    All named variable expressions are lvalues. So what this is doing is taking big_obj, which is an lvalue, coercing it into an xvalue, and using that to initialize an rvalue reference which can then only be used as an lvalue, and we're back to where we started. It's a completely useless line of code.

    Except for function arguments, defining local rvalue references is generally not very useful. They do extend the lifetimes of the temporary objects used to initialize them, though, so they might find occasional use in separating complex expressions across multiple statements.

    Value category is a property of expressions, not of references. You choose which kind of reference to use based on the value category of the expressions you want it to be able to bind to. And you use std::move to force a particular overload of a function to be called where it would otherwise call the wrong one or be ambiguous.

    Bjarne's advice given during a Q&A session at GoingNative2013 was basically, don't try to use rvalue references for anything more "clever" than move constructors and move assignment operators.

    And from Herb Sutter: A lot of people think move means writing && everywhere, but it's not. You don't need to write && yourself, unless you're writing a move constructor or move assignment operator, or perfectly forwarding an argument in a function template.