Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing r-value as non-const reference (VS warning C4239)

What I wish to do (using a C++ lambda) is effectively:

std::vector<MyType> GetTheArray () {return something;}

const auto DoSomething = [](std::vector<MyType> & array)
{
     //Some processing that involves either sorting the 'array' or setting temporary flags on the items
};

DoSomething (GetTheArray ());

This appears to be disallowed in standard C++ because the rvalue cannot be passed as a non-const reference.

My questions:

1) Is there a way to do this using a type-cast or am I obliged to create a temporary variable to store the results of GetTheArray ()?

2) Is there a good reason why this is disallowed in C++?

Please note that the 'something' returned from 'GetTheArray' is an array that is constructed on the fly, not a stored value.

like image 751
Coder_Dan Avatar asked Apr 25 '13 13:04

Coder_Dan


3 Answers

It seems from the comments that what you want is to take a vector, modify it destructively (in the sense that the original state cannot be reset) and then use the result internally. And you want this to work efficiently for both lvalues and rvalues.

The next question is whether in the case of the lvalue, the code that holds the original container needs it after the function call has completed, and if it needs the original state of the vector or not. Depending on the answers to that you have different solutions:

The caller holding an lvalue does not use it anymore after the call

(or alternatively, the caller holding the lvalue needs the original state)

This is the simplest case. Then your function should take the container by value. If the caller has an lvalue, it can std::move to avoid the copy (it does not care about the object anymore) or copy which might be more expensive but leaves the original container untouched.

If the function is called with an rvalue then the copy will be either elided or transformed into a cheap implicit move.

The caller holding the lvalue does not need the original state, but it needs the container

This case is the hard one, and you will need to provide two overloads for the same function (or lambda), one taking an lvalue to be used by this caller and a different taking an rvalue-reference for the case where the caller hands a temporary. In both cases the binding will be cheap. While this requires more code, you can implement one overload in terms of the other:

rtype f(std::vector<Data> & );   // for lvalues
rtype f(std::vector<Data> && v)  // for rvalues
   { return f(v); }              // v is an lvalue here

The fact that you are doing lambdas might make this slightly more complicated, but hopefully not too much.

like image 72
David Rodríguez - dribeas Avatar answered Nov 15 '22 08:11

David Rodríguez - dribeas


If you for some reason you really want to modify something you could always copy the temporary into your lambda.

const auto DoSomething = [](std::vector<MyType> array) { /*whatever*/ }

The way you call your lambda a compiler might be able to elide the copy. C++11 move semantics formalize this.

like image 30
Benjamin Bannier Avatar answered Nov 15 '22 10:11

Benjamin Bannier


(Actually, @honk beat me to it as regards passing-by-value. To be different, I'll extend my answer to discuss using && explicitly)

Drop the &, and just pass it by value instead.

You're using lambdas, therefore you're using c++11, therefore you should just pass it by value. Yes, it will be just as fast.

C++11 is very good at noticing when a temporary is being 'copied'. It will replace the copy with a move. It will then be fast, and arguably it will be easier to read.

So, even though you might know nothing about && and std::move and so on, it's safe to say that you should start moving towards passing things by value instead of by reference. It will lead to more readable code, and it shouldn't lead to a slowdown if used at the right time.

Basically, it's fast if you pass a temporary value, such as that returned by a function, into a function, but only if you are using a standard container such as vector. Otherwise, you'll need to write your own &&-aware constructors to take advantage of the possible speed up.

Update: If you're determined to force it to be by reference, and/or you are using custom containers, then you could replace & with &&. This will allow you to pass a non-const reference. This is called a rvalue-reference.

As has been pointed out by many others, the temporary will be destroyed just after DoSomething returns. Bear this in mind. It means that you will want to fully consume the contents of the array before it returns. Maybe you print it to the screen, or store it in a database. Alternatively, you might copy or move the data into another object and return that.

like image 26
Aaron McDaid Avatar answered Nov 15 '22 10:11

Aaron McDaid