How to ensure the move constructor is used

919 views Asked by At

The code below gives the error:

use of deleted function ‘constexpr B::B(const B&)’

now, I know this happens because the copy constructor is (intentionally) implicitly deleted by specifying a move constructor, and that copying the vector causes the calls to the (deleted) copy constructor. I think I also understand why the vector's copy constructor and assignment operator are used. I clearly want to use the move constructor and assignment operator though: move the object, so also move the vector it contains. So, how do I get my move constructor/assignment operator to use the vector's move constructor/assignment operator?

Here is the code:

#include <vector>

class B {
private:
    /* something I don't want to copy */
public:
    B() {};
    B(B&& orig) {/* move contents */};
    B& operator=(B&& rhs) {
        /* move contents */
        return *this;
    };
};

class A {
private:
    vector<B> vec;
public:
    A() : vec() {};
    A(A&& orig) : vec(orig.vec) {};
    A& operator=(A&& rhs) {
        vec = rhs.vec;
        return *this;
    };
};
3

There are 3 answers

0
Niall On BEST ANSWER

To ensure that the "move" constructor and assignment operators are called, you need to provide an object of the correct value category. The value category is used to determine which operators and constructors could be used.

Use std::move to change the value category (from the lvalue in this case) to a xvalue (an rvalue, that can be moved from).

// ...
A(A&& orig) : vec(std::move(orig.vec)) {};
A& operator=(A&& rhs) {
    vec = std::move(rhs.vec);
    return *this;
};

The move does not copy or change the object in any way, it is simply a cast to an rvalue reference of the argument type - hence modifying the value category.

2
art On

If all the members of your class are the objects of classes with correctly defined move constructor/assignment operator you should better use default move constructor/assignment operator for your class. This would be much easier and less error-prone and ensure the call of move constructor/assignment operator of class members.

In your particular example it would be:

class A
{
private:
    vector<B> vec;
public:
    A() : vec() {};
    A(A&&) = default;
    A& operator=(A&&) = default;
};
0
TartanLlama On

Just call std::move on the vectors in the expression in which you want to move from them:

class A {
private:
    vector<B> vec;
public:
    A() : vec() {};
    A(A&& orig) : vec(std::move(orig.vec)) {};
    //                ^^^^^^^^^
    A& operator=(A&& rhs) {
        vec = std::move(rhs.vec);
        //    ^^^^^^^^^
        return *this;
    };
};

Even though you take in rvalue references, rhs and orig are still lvalues in the function, so you need to call std::move on them.