2

First, I am not good at English, so I use a translator.

This code is the code that supports move semantic in vector.

A code that creates a single vector and inserts a Test object into the push_back() method.

The result of this code is that every time a Test object is added because of a push_back() method, the move constructor should be called, right?

But the compiler calls the move constructor only when new objects are inserted, and the old ones call the copy constructor.

Why is this happening? Am I missing something?

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

class Test
{
public:
    Test(int data1, int data2) :data1(data1), data2(data2) 
    {
        cout << "Normal Constructor" << endl; 
    }
    Test(const Test& src)
    {
        cout << "Copy Constructor" << endl;
        this->data1 = src.data1;
        this->data2 = src.data2;
    }
    Test(Test&& rhs)
    {
        cout << "Move Constructor" << endl;
        this->data1 = rhs.data1;
        this->data2 = rhs.data2;
        rhs.data1 = 0;
        rhs.data2 = 0;
    }
    Test& operator=(const Test& src)
    {
        if (this == &src)
            return *this;
        cout << "Copy assignment operator" << endl;
        this->data1 = src.data1;
        this->data2 = src.data2;
        return *this;
    }
    Test& operator=(Test&& rhs) 
    {
        if (this == &rhs)
            return *this;
        cout << "Move assignment operator" << endl;
        this->data1 = rhs.data1;
        this->data2 = rhs.data2;
        rhs.data1 = 0;
        rhs.data2 = 0;
        return *this;
    }
    private:
        int data1, data2;
    };

int main()
{
    vector<Test> vec;
    for (int i = 0; i < 5; i++)
    {
        cout << "Iteration  " << i << endl;
        vec.push_back(Test(100, 100));
        cout << endl;
    }
}
O'Neil
  • 3,790
  • 4
  • 16
  • 30
kwanhh25
  • 29
  • 3

1 Answers1

4

The reason std::vector chooses the copy constructor instead of the move constructor is that your move constructor isn't nothrow. Why does std::vector care?

Well, std::vector has some exception safety guarantees. See, for example, std::vector::push_backs exception guarantee:

If an exception is thrown (which can be due to Allocator::allocate() or element copy/move constructor/assignment), this function has no effect (strong exception guarantee).

This means that when a exception is thrown somehow, it doesn't blow up the whole container, and in this case a strong exception guarantee even guarantees you that a bad insertion has no impact at all on the vector.

Having a non-noexcept move constructor is a problem, because if a move would throw on reallocation, std::vector could not go back to a valid state as it might have already moved half of its items over but not the other half, and trying to move them back to the old memory location might throw as well. Copying doesn't have this problem as the old items stay intact, so if a copy would throw, std::vector just needs to destruct all (new) items it has copied so far and give up. You, as a user of the collection, have the guarantee that a bad insertion or reallocation leaves all other items intact. This is why it will only use the move-constructor if it is guaranteed the move can never throw, which is promised by noexcept.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
tkausl
  • 13,686
  • 2
  • 33
  • 50
  • Thank you for your reply. It was very helpful. To ask you one more question, I knew that std::vector didn't use a move constructor without a noexcept. By the way, why does a compiler use a move constructor to add new elements? The move constructor was not declared noexcept. – kwanhh25 Mar 14 '19 at 02:25
  • It can move new elements in because at this point it doesn't really matter whether it works or not. The reallocation already happened and succeeded, so a exception on the move-construction doesn't touch the other elements at all. The problem with reallocation is to make sure either all or no elements are moved, not only some fraction. You don't have this problem with a single element. – tkausl Mar 14 '19 at 02:31