Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't C++ move construct rvalue references by default? [duplicate]

Say I have the following function

void doWork(Widget && param)  // param is an LVALUE of RRef type
{
    Widget store = std::move(param); 
}

Why do I need to cast param back to an rvalue with std::move()? Shouldn't it be obvious that the type of param is rvalue since it was declared in the function signature as an rvalue reference? Shouldn't the move constructor be automatically invoked here on this principle alone?

Why doesn't this happen by default?

like image 300
barney Avatar asked Aug 23 '17 15:08

barney


People also ask

Can rvalue be copied?

Yes, rvalues are moved, lvalues are copied. But when there's no according move operation, rvalues are copied as well.

Can you modify an rvalue?

rvalue references have two properties that are useful: rvalue references extend the lifespan of the temporary object to which they are assigned. Non-const rvalue references allow you to modify the rvalue.

Can lvalue bind to rvalue reference?

An lvalue reference can bind to an lvalue, but not to an rvalue.

Can rvalue be const?

So an rvalue can be used both with rvalue overloads and a const lvalue reference.


1 Answers

with your design:

void doWork(Widget && param)
{
    Widget store1 = param;     // automatically move param
    Widget store2 = param;     // boom

    Widget store_last = param; // boom    
}

with current design:

void doWork(Widget && param)
{
    Widget store1 = param;                // ok, copy
    Widget store2 = param;                // ok, copy

    Widget store_last = std::move(param); // ok, param is moved at its last use
}

So the moral here is that even if you have an rvalue reference you have a name for it which means you can use it multiple times. As such you can't automatically move it because you could need it for a later use.


Now let's say you want to re-design the language so that the last use is automatically treated as an rvalue.

This can be easily done in the above example:

void doWork(Widget && param)
{
    Widget store1 = param;     // `param` treated as lvalue here, copy
    Widget store2 = param;     // `param` treated as lvalue here, copy

    Widget store_last = param; // `param` treated as rvalue here, move    
}

Let's ignore the inconsistency of how param is treated (which in itself is a problem).

Now think what use of param is the last use:

void doWork(Widget && param)
{  
    Widget store2 = param;        // this can be last use or not

    while (some_condition())
    {
         Widget store1 = param;   // this can be both last use and not
    }
}

The language simply cannot be designed this way.

like image 68
bolov Avatar answered Nov 15 '22 21:11

bolov