Confusion about copy constructor vs. move constructor being called when std::vector is resized

66 views Asked by At

I have the following situation: A tuple containing a custom class is stored in a vector.

struct A {
    string m_name;

    void cp(const A& x)        { m_name = x.m_name;       cout << m_name << ": copy "; }
    void mv(A&& x)             { m_name = move(x.m_name); cout << m_name << ": move "; }

    A(const string& n) : m_name(n)          { cout << "ctor" << endl; }
    A(A&& x)                   { mv(move(x)); cout << "ctor" << endl; }
    A(const A& x)              { cp(x);       cout << "ctor" << endl; }
    A& operator=(A&& x)        { mv(move(x)); cout << "ass-op" << endl; return *this; }
    A& operator=(const A& x)   { cp(x);       cout << "ass-op" << endl; return *this; }
    ~A() noexcept              { cout << m_name << ": dtor " << endl; }
};

using t_tuple = tuple<string, A>;
vector<t_tuple> v;

void store_in_vector(const string& s, A&& a) {
    v.emplace_back(s, move(a));
}

On insert elements in the vector, I recognized that the copy constructor of A is being called whenever the std::vector has to resize itself internally.

Here is my test code:

store_in_vector("a", A("a"));
store_in_vector("b", A("b"));
store_in_vector("c", A("c"));
store_in_vector("d", A("d"));
store_in_vector("e", A("e"));
store_in_vector("f", A("f"));
store_in_vector("g", A("g"));
store_in_vector("h", A("h"));
store_in_vector("i", A("i"));
store_in_vector("j", A("j"));
store_in_vector("k", A("k"));
store_in_vector("l", A("l"));

The output looks like follows after inserting the "b" element

ctor
b: move ctor
a: copy ctor
a: dtor
: dtor

Why is the copy constructor being called? Wouldn't it be more optimal if std::vector called the MOVE constructor? What I am confused about is the following: If I change struct A to the following implementation which deletes the copy constructor, it actually calls the MOVE constructor when the vector is resized.

Here is the modified implementation of struct A:

struct A {
    string m_name;

    void cp(const A& x)        { m_name = x.m_name;       cout << m_name << ": copy "; }
    void mv(A&& x)             { m_name = move(x.m_name); cout << m_name << ": move "; }

    A(const string& n) : m_name(n)          { cout << "ctor" << endl; }
    A(A&& x)                   { mv(move(x)); cout << "ctor" << endl; }
    A(const A& x) = delete;
    A& operator=(A&& x)        { mv(move(x)); cout << "ass-op" << endl; return *this; }
    A& operator=(const A& x) = delete;
    ~A() noexcept              { cout << m_name << ": dtor " << endl; }
};

which shows this output after inserting element "b":

ctor
b: move ctor
a: move ctor
: dtor
: dtor

Everything seems to work normally, and in my optnion, the code calling the move constructor should be more optimal (i.e. faster, less overhead). However, I don't understand why this happens. How could I change the implementation of struct A to get the move constructor called without deleting the copy constructor? If there is any good reason why the copy constructor gets called in the frist version, please explain!

I am using Xcode with Apple LLVM 6.1

0

There are 0 answers