Cannibalize just some of the rvalue ref resources

My intention is this: get the rvalue link (i.e. the link to the object I want to use), delete some of its resources, and then return the other resources. I wrote this code:

std::vector<int> dosomething(std::vector<int> && vec) { // do something with vec (eg consume SOME of its resources) return std::move(vec); } 

but I'm not sure that:

  • Is this the right way to achieve what I want?
  • Should I use std::forward ? I believe not, as this is not a universal reference.
+8
c ++
source share
3 answers

I assume that the std::vector<int> parameter is a placeholder for a more complex type like std::vector<Resource> . You cannot move int , and the only other resource you could get would be the data of the vector itself.

  • Is this the right way to achieve what I want?

Yes. If you omit std::move , vec will be considered an lvalue and it will be copied to return.

You can observe this behavior in this example:

 struct C { C() {} C( C const& ) { std::cout << "copy\n"; } C( C&& ) { std::cout << "move\n"; } }; C f( C&& c ) { return std::move( c ); // cast to rvalue reference, invokes the move ctor } C g( C&& c ) { return c; // does not cast to rvalue reference, will invoke the copy ctor } int main() { f( C{} ); // calling with rvalue, the return causes the move constructor to be called. g( C{} ); // calling with rvalue, but no std::move means calling the copy constructor. } 
  1. Should I use std :: forward? I believe not, as this is not a universal reference.

Not. This will not hurt, since std::forward<T> conditionally discards (lvalues โ€‹โ€‹are not thrown by rvalue links, rvalues โ€‹โ€‹will be). However, it will basically achieve the same value as std::move with lots of input.

Typically, you use std::move with rvalues โ€‹โ€‹and std::forward<T> with generic links.

+2
source share

Is this the right way to achieve what I want?

OK, let it ignore the fact that vector<int> not a type where it is reasonable to hide with only some of its resources (it has only one resource: distribution). I pretend that you are talking about some type that actually has several resources.

I would consider such a function as distorting itself.

A function that declares that it accepts a type as a reference to rvalue says something very important about what it will do with this value. He says that he will either directly or indirectly move this object to another object of this type. He can do it himself, or he can call someone else. But this will happen before the function returns.

The idea is for this code to mean what it says:

 SomeFunction(std::move(value)); 

It looks like value moving into SomeFunction , just as if you were doing SomeType t = std::move(value); . In both cases, the state of value must be moved. Otherwise, the code gets confused.

A function that wants to arbitrarily change a parameter, leaving it in some new state, must accept the parameter as a reference to not the lvalue constant, but not the rvalue reference. Exactly what โ€œmodifyโ€ means depends on the function and what it needs to do. If you want to hide with some resources, and not with others, you can do this using a non const const link. You can even std::move from a subobject of a constant reference without a reference. But you and your users must understand what the state of the object will be after the function call.

When you take a parameter from an rvalue link, it is short for "when I return, this object will be in the" moved "state.) Please do not distort rvalue links as links that do not contain constants.

+5
source share

My intention is this: get the rvalue link (i.e. the link to the object I want to use), delete some of its resources and then return the other resources.

This is normal, but probably better:

  • completely remove resource placeholders or

  • replace remote resources with other objects or

  • tells the caller which resources have been deleted.

Because if you do not do any of them, the user can use one of the moved elements that you cannot use. Since relocated objects (at least in the STL) have a valid but undefined state, using these cannibalised objects will have undefined results.

You do not want this.

Is this the right way to achieve what I want?

Seems good

Should I use std :: forward? I believe not, as this is not a universal link

No, not worth it. Your reasoning is correct.

FYI: "indicating to users whose resources have been deleted" has a model in the standard library. Take a look at std::remove_if - it actually moves the "deleted" elements to the end of the sequence and returns an iterator with end marks of the remaining elements and the beginning of the "deleted" ones.

In this case, I could approach the problem.

goal: do something with all the lines lexically larger than the "ccc" in the vector, leaving the rest in the vector in the original order

 template<class T> void do_something_with(T t) { std::cout << "did something with " << t << std::endl; } template<class ForwardIt, class UnaryPredicate> ForwardIt rotate_if(ForwardIt first, ForwardIt last, UnaryPredicate p) { while(first != last) { auto n = std::next(first); if (p(*first)) { if (n != last) { std::rotate(first, n, last); first = n; last = std::prev(last); } else { last = first; break; } } first = n; } return last; } template<class T, class Pred, class Model> std::vector<T> do_something_if(std::vector<T>&& in, Pred pred, Model&& model) { auto result = std::move(in); auto ibreak = rotate_if(std::begin(result), std::end(result), [&](auto& v) { auto match = pred(v, model); return match; }); for (auto i = ibreak ; i != std::end(result) ; ++i) { do_something_with(std::move(*i)); } result.erase(ibreak, std::end(result)); return result; } void test() { std::vector<std::string> words = { "yyy", "aaa", "zzz", "bbb", }; auto filtered = do_something_if(std::move(words), std::greater<>(), "ccc"); std::cout << "remaining items:\n"; for (auto& w : filtered) { std::cout << w << std::endl; } } 

expected output:

 did something with zzz did something with yyy remaining items: aaa bbb 

In most cases, there will be more effective solutions, but this will avoid memory allocation.

+3
source share