Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

(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.

You are handling a temporary returned from the GetTheArray() call in your bottom line. This can only bind to a const reference, and then the objec's lifetime will be extended to the lifetime of the const reference it is bound to (hence "binding").

Even if you could do what you wanted, your call to DoSomething would still be an expensive no-op. At the very least you'd need to return something from DoSomething.

C++ forbids this because (since C++11) it has rvalue references with move semantics, which can do awesome stuff, but not without returning something.

If you need to modify something array you need to return a reference to it. Otherwise don't pass it by (non const) reference to DoSomething
The reason why it is disallowed is to avoid problems you are illustrating here. It looks like you are going to modify something, but instead copy of something returned by GetTheArray() will be changed. Which doesn't make much sense since this copy will be deleted right after DoSomething exits.
You have 3 ways to solve this.
1. You need to modify something then change GetTheArray to return reference
2. You need to modify copy of something that will be discarded DoSomething did it's job then change DoSomething to accept array by value.
3. You don't need to modify something in DoSomething then const reference as param of DoSomething should be used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top