vector push_back calling copy_constructor more than once?

Solution 1:

What happens:

  1. x is inserted via push_back. One copy occurs: The newly created element is initialized with the argument. my_int is taken over as zero because xs default constructor initialized it so.

  2. The second element is push_back'd; The vector needs to reallocate the memory since the internal capacity was reached.
    As no move constructor is implicitly defined for Myint1 the copy constructor is chosen; The first element is copied into the newly allocated memory (its my_int is still zero... so the copy constructor shows my_int as 0 again) and then x is copied to initialize the second element (as with the first in step 1.). This time x has my_int set to one and that's what the output of the copy constructor tells us.

So the total amount of calls is three. This might vary from one implementation to another as the initial capacity might be different. However, two calls are be the minimum.

You can reduce the amount of copies by, in advance, reserving more memory - i.e. higher the vectors capacity so the reallocation becomes unnecessary:

myints.reserve(2); // Now two elements can be inserted without reallocation.

Furthermore you can elide the copies when inserting as follows:

myints.emplace_back(0);

This "emplaces" a new element - emplace_back is a variadic template and can therefore take an arbitrary amount of arguments which it then forwards - without copies or moves - to the elements constructor.

1 Because there is a user-declared copy constructor.

Solution 2:

You got it...it was the resizing. But I'll just point out that if you're doing some bean counting on your constructors, you might be interested in "emplacement":

#include <iostream>
#include <vector>
using namespace std;

class Myint
{
private:
    int my_int;
public:
    explicit Myint(int value = 0) : my_int(value) 
    {
        cout << "Inside default " << endl;
    }
    Myint(const Myint& x) : my_int(x.my_int)
    {
        cout << "Inside copy with my_int = " << x.my_int << endl;
    }
    Myint(const Myint&& x) noexcept : my_int(x.my_int) {
        cout << "Inside move with my_int = " << x.my_int << endl;
    } 
};

int main() {
    vector<Myint> myints;
    myints.reserve(2);

    myints.emplace_back(0);
    myints.emplace_back(1);

    // your code goes here
    return 0;
}

That should give you:

Inside default 
Inside default

And, due to the noexcept on the move constructor...if you delete the reserve you'd get a move, not a copy:

Inside default 
Inside default 
Inside move with my_int = 0

There's no real advantage with this datatype of a move over a copy. But semantically it could be a big difference if your data type was more "heavy weight" and had a way of "moving" its members that was more like transferring ownership of some pointer to a large data structure.

Solution 3:

When the size of the vector is increased with the second push_back, the existing contents of the vector must be copied to a new buffer. To verify, output myints.capacity() after the first push_back, it should be 1.

Solution 4:

This depends on how much memory was reserved to an object of type std::vector. It seems that when push_back was first executed there was allocated memory only for one element. When the second time push_back was called the memory was reallocated to reserve memory for the second element. In this case the element that is already in the vector is copied in the new place. And then the second element is also added.

You could reserve enough memory yourself that to escape the second call of the copy constructor:

vector<Myint> myints;
myints.reserve( 2 );