2

What does it mean that implicit move constructor does a member-wise move and implicit move assignment operator a member-wise assignment?

From https://en.cppreference.com/w/cpp/language/move_constructor:

For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument. If this satisfies the requirements of a constexpr constructor, the generated move constructor is constexpr.

From https://en.cppreference.com/w/cpp/language/move_assignment:

For non-union class types (class and struct), the move assignment operator performs full member-wise move assignment of the object's direct bases and immediate non-static members, in their declaration order, using built-in assignment for the scalars, memberwise move-assignment for arrays, and move assignment operator for class types (called non-virtually).

Will the implicit members look like this for the following exemplary class template:

template<class T>
class Holder {
public:
    Holder(int size) : m_size(size) { m_data = new T[m_size]; }

    Holder(Holder && other) :
        m_size(std::move(other.m_size)),
        m_data(std::move(other.m_data))
    {}

    Holder& operator=(Holder && other) {
       if(this == &other) return *this;
       m_data = std::move(other.m_data);
       m_size = std::move(other.m_size);
       return *this;
    }

    ~Holder() { delete [] m_data; }
private:
    T* m_data;
    int m_size;
};

What's more, what will the std::move() in the above example transfer the resources?

4
  • 1
    If you were to implement a move constructor that individually moves each and every base as well as each and every member - and does that in the initialiser list - you would achieve exactly that is meant by a "full member-wise move". Similarly if you implement the move assignment operator to move all bases and all members. Commented May 20, 2020 at 13:25
  • And moving a raw pointer (like your m_data) will just result in a copy of the pointer which usually breaks the program. m_data(std::exchange(other.m_data, nullptr)) would probably be the correct thing to do in your case. Commented May 20, 2020 at 13:31
  • Yes, but I mean what the implict destructor and copy operator will do? Commented May 20, 2020 at 13:33
  • When a Holder is destroyed, the space that a T* and an int takes up will be freed. Without an explicit destructor, any data pointed at by m_data will be leaked. It will not delete[] it for you. The implicit copy operator will just copy the values. It'll not copy what m_data points at. If you use this, you'll have two objects pointing at the same data and it'll be free'd twice (UB). Commented May 20, 2020 at 13:38

1 Answer 1

2

If you look further down you linked page, you will see that your classes compiler generated move constructor (and move assignment operator) will actually be Trivial:

Trivial move constructor

The move constructor for class T is trivial if all of the following is true:

  • it is not user-provided (meaning, it is implicitly-defined or defaulted);
  • T has no virtual member functions;
  • T has no virtual base classes
  • the move constructor selected for every direct base of T is trivial;
  • the move constructor selected for every non-static class type (or array of class type) member of T is trivial;

A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.

(Emphasis mine)

The two member variables are POD types and therefore are trivially movable. Since your class is not virtual and it holds no non-trivial members it is therefore trivial and all the data members will be copied. As mentioned in the comments, this will lead to double deleting your pointer and UB.

Since this is the case, you need to implement your move semantics properly, by taking ownership of the moved objects pointer and setting it to nullptr. Or better yet, just use std::vector or even std::unique_ptr.

Sign up to request clarification or add additional context in comments.

10 Comments

Ok thanks. One question to that - will this implicit move constructor be defined the same way I have presented in the question but just using the std::move() all the members will take a copy of corresponding members of passed temporary object?
No, the behaviour will be similar, but as specified, you do not use std::move . From the quote: makes a copy of the object representation as if by std::memmove.
Ok, so if I would add some virtual member function what will change?
Might be worth another question, but it will be roughly the same. Your classes move constructor will no longer be trivial but the member variables will be, so the pointer is still just copied and you still have the same problem.
But how the move constructor would look like then? Will it be then the same as in the question?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.