Reputation: 342
I wrote the following code to understand the move sementics of std::vector
class PointerHolder
{
public:
PointerHolder()
{
cout << "Constructor called" << endl;
}
//copy constructor
PointerHolder(const PointerHolder& rhs)
{
cout << "Copy Constructor called" << endl;
}
//copy assignment operator
PointerHolder& operator = (const PointerHolder& rhs)
{
cout << "Copy Assignment Operator called" << endl;
return *this;
}
// move constructor
PointerHolder(PointerHolder&& rhs)
{
cout << "Move Constructor called" << endl;
}
// move assignment operator
PointerHolder& operator = (PointerHolder&& rhs)
{
cout << "Move Assignment Operator called" << endl;
return *this;
}
};
void processVector(std::vector<PointerHolder> vec)
{
}
int main()
{
vector<PointerHolder> vec;
PointerHolder p1;
PointerHolder p2;
vec.push_back(p1);
vec.push_back(p2);
cout << "Calling processVector\n\n" << endl;
processVector(std::move(vec));
}
Since I pass an Rvalue reference of the vector When calling processVecor, what Actually should get called is the move constructor of the std::vector when the function parameter object is formed. Is that right ?
So I expected the move constructor of the vecor within itself woud call the move constructor of the PointerHolder class.
But there was no evidence printed to confirm that.
Can you please clarify the behaviour. Doesn't the move constructor of the std::vector in turn call the move constructor of the individual items
Upvotes: 0
Views: 2639
Reputation: 11317
No, it doesn't require the elements to be moved when calling the move constructor of the std::vector
. To understand why, I think you should have a good mental model of how std::vector
is implemented. (Most implementations look like this, except they need some more complexity for dealing with allocators)
std::vector
?In the simplest form, it has 3 members:
The size indicates how many elements in use, the capacity indicates how much data fits in the currently allocated data. Only when your new size would become larger than the capacity, the data needs to be reallocated.
The data that is allocated is uninitialized memory, in which in-place the elements get constructed.
So, given this, implementing the move of a vector would be:
With this, the new instance is completely valid. The old one is in a valid but unspecified state
. This last one means: you can call the destructor without crashing the program.
For vector, you can also call clear
to bring it back to a valid state, or the operator=
.
Given this model, you can easily explain all operators. Only move-assignment is a bit more complex.
Upvotes: 2
Reputation: 19
If we're looking at the standard (https://en.cppreference.com/w/cpp/container/vector/vector), it says, moving requires constant amount of time O(1).
Regardless of that, if we're looking at most common implementations, std::vector is a dynamic array. A dynamic array first allocates for example space for 8 elements. If we need space for 9 that this 8 is multiplied or increased by a specific amount, often multiplied bei 1.44, 2 or sth. like that.
But as concerns the moving aspect: what are our member variables and how do we move them? Well, the dynamic array is just - as mentioned - a pointer to the first element and if we wanna move the structure we'll copy the pointer to the other object and set the old pointer to nullptr (or NULL if you don't care for the issue for what nullptr has been implemented for, nullptr is obviously preferrable). And of course things like internal saved size (if saved) has to be copied and in the old object has to be set to zero as well (or whatever move semantics are there).
Upvotes: 0
Reputation: 461
No. It doesn't call the move constructor. To call move constructor of element you will have to call std::move while pushing to vector itself.
int main()
{
vector<PointerHolder> vec;
PointerHolder p1;
PointerHolder p2;
vec.push_back(std::move(p1));
vec.push_back(p2);
cout << "Calling processVector\n\n" << endl;
processVector(std::move(vec));
}
output
Constructor called
Constructor called
Move Constructor called
Copy Constructor called
Calling processVector
Upvotes: 0
Reputation: 137415
No. It pilfers the entire block of memory with all the elements instead. Why bother moving the contents when you can just grab the whole thing?
(The allocator-extended move constructor will need to perform a memberwise move if the supplied allocator compares unequal to the source vector's allocator.)
Upvotes: 2
Reputation: 172994
No. Note that the complexity requirement of move contructor of std::vector
is constant.
Complexity
6) Constant.
That means the move contructor of std::vector
won't perform move operation on every individual elements, which will make the complexity to be linear (like copy constructor). The implementation could move the inner storage directly to achieve it.
Upvotes: 2