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.
3 Answers 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;
...
};
Comments
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.
2 Comments
int? Also arguably derp and std::vector<int> are just a collection of integers, yet moving and copying them are very different.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.
std::moveis only a cast.Valis implemented. If it makes good use of move semantics you gain something, otherwise you don't. As the author ofValyou should try to make good use, otherwise you don't care.std::vector's move constructor has nothing to do with it because it is aValthat is being moved, not astd::vector.