3mr
3mr

Reputation: 342

Does the move constuctor of std::vector call the move constructor of the items

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

Answers (5)

JVApen
JVApen

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)

So what is std::vector?

In the simplest form, it has 3 members:

  • A capacity: (size_t)
  • A size: (size_t)
  • A pointer to data (T*, std_unique_ptr, void*)

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:

  • Copy over capacity/size
  • Copy over the pointer and set to nullptr in original (same behavior as unique_ptr)

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

iBinary
iBinary

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

rakesh.sahu
rakesh.sahu

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

T.C.
T.C.

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

songyuanyao
songyuanyao

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

Related Questions