2

I'm trying to call some function that includes adding element to vector ( argument passed by value ):

std::vector<Val> vec; 
void fun( Val v )
{
 ...
 vec.push_back(std::move( v ));
 ...
}

The question is: are there any benefits from move semantics?

I'm confused about variable will be "moved" from stack to heap when new vector element constructing: it seems it will be just copied instead.

Raul Cacacho
2671 gold badge4 silver badges16 bronze badges
asked Mar 2, 2018 at 14:41
11
  • std::move is only a cast. Commented Mar 2, 2018 at 14:43
  • 1
    @liliscent Yeah, I know. I'm interested about vector move constructor behaviour in this situation Commented Mar 2, 2018 at 14:47
  • 1
    It depends on how Val is implemented. If it makes good use of move semantics you gain something, otherwise you don't. As the author of Val you should try to make good use, otherwise you don't care. Commented Mar 2, 2018 at 14:47
  • 2
    std::vector's move constructor has nothing to do with it because it is a Val that is being moved, not a std::vector. Commented Mar 2, 2018 at 14:48
  • @nwp Let's assume Val is std::shared_ptr. Seems it's OK to move shared pointers, and could improve performance sometimes. I'm confused about moving stack variable to heap exactly Commented Mar 2, 2018 at 14:49

3 Answers 3

3

Whether there is any benefit by moving instead of copying actually depends on Val's implementation.

For example, if Val's copy constructor has a time complexity of O(n), but its move constructor has a complexity of O(1) (e.g.: it consists of just changing a few internal pointers), then there could be a performance benefit.


In the following implementation of Val there can't be any performance benefit by moving instead of copying:

class Val {
 int data[1000];
...
};

But for the one below it can:

class Val {
 int *data;
 size_t num;
...
};
answered Mar 2, 2018 at 14:48
Sign up to request clarification or add additional context in comments.

Comments

2

If std::move allows the underlying objects to move pointers from one to the other; rather than (what in done in copying) allocating a new area, and copying the contents of what could be a large amount of memory. For example

class derp {
 int* data;
 int dataSize;
}

data & dataSize could be just swapped in a move; but a copy could be much more expensive

If however you just have a collection of integers; moving and copying will amount to the same thing; except the old version of the object should be invalidated; whatever that should mean in the context of that object.

answered Mar 2, 2018 at 14:47

2 Comments

What do you mean by "the old version should be invalidated"? How do you invalidate an int? Also arguably derp and std::vector<int> are just a collection of integers, yet moving and copying them are very different.
@nwp how do you invalidate an int? It's the object you invalidate; not the individual members. So an int as you say can't be; but if that int represents the datasize like above then having it 0'ed might make sense; and having data (again as above) set to nullptr makes even more sense.
1

A move is a theft of the internal storage of the object, if applicable. Many standard containers can be moved from because they allocate storage on the heap which is trivial to "move". Internally, what happens is something like the following.

template<class T>
class DumbContainer {
private:
 size_t size;
 T* buffer;
public:
 DumbContainer(DumbContainer&& other) {
 buffer = other.buffer;
 other.buffer = nullptr;
 }
 // Constructors, Destructors and member functions
}

As you can see, objects are not moved in storage, the "move" is purely conceptual from container other to this. In fact the only reason this is an optimization is because the objects are left untouched.

Storage on the stack cannot be moved to another container because lifetime on the stack is tied to the current scope. For example an std::array<T> will not be able to move its internal buffer to another array, but if T has storage on the heap to pass around, it can still be efficient to move from. For example moving from an std::array<std::vector<T>> will have to construct vector objects (array not moveable), but the vectors will cheaply move their managed objects to the vectors of the newly created array.

So coming to your example, a function such as the following can reduce overhead if Val is a moveable object.

std::vector<Val> vec; 
void fun( Val v )
{
 ...
 vec.emplace_back(std::move( v ));
 ...
}

You can't get rid of constructing the vector, of allocating extra space when it's capacity is filled or of constructing the Val objects inside the vector, but if Val is just a pointer to heap storage, it's as cheap as you can get without actually stealing another vector. If Val is not moveable you don't lose anything by casting it to an rvalue. The nice thing about this type of function signature is that the caller can decide whether they want a copy or a move.

// Consider the difference of these calls
Val v;
fun(v);
fun(std::move v);

The first will call Val's copy constructor if present, or fail to compile otherwise. The second will call the move constructor if present, or the copy constructor otherwise. It will not compile if neither is present.

answered Mar 2, 2018 at 15:19

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.