Does std::vector.push_back(std::move(foo)) make sense?
Asked Answered
M

2

9

I have come across this in some code (details eliminated for clarity):

std::vector<std::vector<int>> foo;
{
    std::vector<int> bar = {42};
    foo.push_back(std::move(bar)); // Hmmm...
} // Indicate `bar` is no longer needed.

The std::move looks unnecessary to me, but is it? Is the behaviour any different from just foo.push_back(bar);? What if instead of an int the element is a class such as pcl::PointXYZ as it is in my actual code?

UPDATE: I have altered the code to more explicitly indicate that bar is not used after the std::move, so there is no illegal access, etc, risk.

Moultrie answered 8/5, 2018 at 6:26 Comment(0)
S
12

Class vector has two push_back implementations:

void push_back( const T& value );
void push_back( T&& value );

The first one does copy of the element given.

The second tries to "move" it by calling element's move constructor (if it's defined).

Using move forces to pick the second implementation which is supposed to reuse the value rather than just copying one.

In this particular case this is what gonna happen:

  1. Vector bar is allocated on the stack, but its elements (42) are allocated on the heap.
  2. When you call foo.push_back(...), foo allocates on the heap a new vector, which is gonna be bars copy. Let's call it baz :) Depending on which push_back implementation is called the following will happen then:
    • void push_back( const T& value );: in this case all bar's elements will be copyed to baz as well.
    • void push_back( T&& value ); in this case baz will receive a pointer to bar's elements, so no copy operations are performed. But it is crucial for understanding that bar will be deprived of its elemets (now baz owns them), so bar shoun't be used after move.

It isn't that important what kind of the elements are (plain ints or pcl::PointXYZ), since only the first vector has allocated the memory for the elements, and the pointer to that memory is the only thing that is copyed during the move call.

Suitcase answered 8/5, 2018 at 6:29 Comment(3)
So the answer is yes, it is necessary (for suitable values of "necessary") because yes, the behaviour is different as we should save a copy of the memory containing 42. However, would I be correct in saying that a smart compiler is allowed to and could figure out the optimisation, but std::move makes it explicit that the optimisation must take place?Moultrie
@KenY-N, yes. Not only it makes it explicit but also makes it work. (The "optimization" wouldn't take place without the std::move). You can even use emplace. foo.empace(std::move(bar));. If the code is that simple (no uses of var before push_back) you can foo.emplace(std::vector{42}) or even foo.emplace({42}).Fanti
@Fanti ..and I guess, in most cases the use of var might be postponed to point after emplace, because emplace is meant to return iterator?Elidaelidad
B
1

The std::move looks unnecessary to me, but is it?

It depends on your intention.

Is the behaviour any different from just foo.push_back(bar);?

Yes, foo.push_back(bar); will copy bar into foo (potentially a performance penalty as std::vector deals with dynamic allocations). This will also leave bar the same and you can use it afterwards.

On the other hand, foo.push_back(std::move(bar)); makes no copies and reuses the already allocated memory in bar. Note that this leaves bar in a valid but unspecified state after the move (aka you can't use it unless you reinitialize/reassign it).

What if instead of an int the element is a class such as pcl::PointXYZ as it is in my actual code?

Move-semantics are only useful for class-types which make use of dynamic allocations (owning pointers). pcl::PointXYZ and int are no such classes, so it makes no sense to std::move an int or std::move a pcl::PointXYZ.

Beebe answered 8/5, 2018 at 6:39 Comment(2)
Note, it's actually an std::vector of int that I am moving.Moultrie
@KenY-N That does not matter. Memory is still allocated for an int. Moving a std::vector<int> makes sense in that regard.Beebe

© 2022 - 2024 — McMap. All rights reserved.