1

I have the following code:

#include <vector>
#include <iostream>

struct Data
{
    Data() = default;

    Data(const Data& other)
    {
        std::cout << "copy ctr" << std::endl;
    }

    Data(Data&& other)
    {
        std::cout << "move ctr" << std::endl;
    }
};

int main(int argc, char** argv)
{
    std::vector<Data> vector;
    for (size_t i = 0u; i < 100u; ++i) {
        vector.push_back(Data{});
    }

    return EXIT_SUCCESS;
}

With the following output:

move ctr move ctr copy ctr move ctr copy ctr copy ctr move ctr move ctr copy ctr copy ctr copy ctr copy ctr move ctr move ctr move ctr move ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr copy ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr move ctr

Why I have so much move operations???

My copy and move constructor aren't noexcept...

QuickDzen
  • 247
  • 1
  • 11

3 Answers3

3

This is due to reallocations of the storage for the std::vector. Each time you push_back, if the capacity of the container is not big enough, there's a reallocation. It requires a copy/move of all the current elements into the bigger storage.

There's a discussion here about using move constructor whenstd::vector grows: How to enforce move semantics when a vector grows?.

It is suggested there that adding noexcept will inform the std::vector to use move semantics (with some links to cppreference). I confirmed that is so in your case in MSVC.

In order to avoid it you can use std::vector::reserve. It will reserve storage for the push_backs.

Add the following line before the loop in your main:

vector.reserve(100u);

Now you'll see that only move constructor is called, once for each push_back.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • There are 0 calls to "move ctr" when the vector is resized. The resize calls the "copy ctr". – Goswin von Brederlow May 14 '22 at 11:23
  • I understood from this post mentioned in my answer [https://stackoverflow.com/questions/8001823/how-to-enforce-move-semantics-when-a-vector-grows] that making the move ctor `noexcept` will cause moving instead of copying in resize. Tested it to be so in MSVC. I thought the OP wonders about the copy ctor calls and would like to avoid them, and I suggested 2 ways to do it from different angles (`noexcept` and `reserve`). – wohlstad May 14 '22 at 12:04
1

When std::vector needs more space, it allocates a new array and copies/moves all the existing elements from the old array to the new array. In your example, this reallocation may happen when push_back is used and the capacity of the old vector is not enough to hold all the elements(old + new).

Solution 1
To minimize this, use the std::vector::reserve() member function before pushing elements onto the vector. This pre-allocates the needed space.

std::vector<Data> myVector;
myVector.reserve(100u);
for (size_t i = 0u; i < 100u; ++i) {
        myVector.push_back(Data{});
    }

Solution 2
Or you can create the vector to be of a particular size, like:

std::vector<Data> myVector(100u); //create a vector of size 100 where all elements are default initialized
Jason
  • 36,170
  • 5
  • 26
  • 60
  • There are 0 calls to "move ctr" when the vector is resized. The resize calls the "copy ctr". – Goswin von Brederlow May 14 '22 at 11:23
  • @GoswinvonBrederlow The program uses `push_back` so when reallocation happens the elements will be copied/moved. – Jason May 14 '22 at 11:27
  • They will be copied as seen by the 127 "copy ctr" in the output. There is no move assignment operator for the vector to move the element. And the question was about the "move ctr", which is only used to create the objects the first time. – Goswin von Brederlow May 14 '22 at 11:31
1

I'm not sure what you expected. I counted your output like this:

$ foo | tr " " "\n" | sort | uniq -c
127 copy
227 ctr
100 move

You call vector.push_back 100 times with a temporary object. Since that is movable it gets forwarded to vector.emplace_back which in turn forwards the construction of the objects to "move ctr". 100 calls, 100 "move ctr". That's exactly the number of calls you should expect.

The only thing you should be asking about are the 127 calls to "copy ctr". Those are the bad ones. And the reason for them is that as you push elements into the vector it has to resize the storage. Since your class lacks a move assignment operator the objects have to be copied on resize. You should add Data & operator=(Data &&rhs) so elements can be moved.

You can avoid the resize altogether by making the vector big enough from the start:

vector.reserve(100);
Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42