Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a c++11 move function for a user-defined class?

I have a user-defined class (tree structure) with implemented move semantics, and a swap function. I would like to implement a move function the proper way, working as standard std::move implementation.

In the tree node class, each child node has a parent pointer, pointing to the parent node. This means that for move operations, all children have to reparented (and there may be many children)

This means that use of swap for moving is not optimal, as children of both lists have to be reparented after being swapped. So I would like to implement a move function which clears the moved-from tree.

The declaration of std::move implementations is somewhat complicated, they use a std::remove_reference<T>::type&& return type. Do I need this?

like image 484
galinette Avatar asked Dec 20 '22 15:12

galinette


1 Answers

You don't need to write a specialisation of std::move.

If you write a correct move constructor and move assignment operator, std::move will work on your class.

example:

#include <iostream>
#include <cstring>

using namespace std;

struct Thing {
    Thing()
    : _data(new int[100])
    {
        cout << "default construct\n";

    }

    // Copy operator
    Thing(const Thing& other)
    : _data(new int[100])
    {
        cout << "copy constructor\n";
        memcpy(_data, other._data, sizeof(int) * 100);
    }

    // Move constructor
    Thing(Thing&& other) noexcept
    : _data(other._data)
    {
        cout << "move constructor\n";
        other._data = nullptr;
    }

    // assignment operator
    Thing& operator=(const Thing& rhs) {
        cout << "copy operator\n";
        if (&rhs != this) {
            Thing tmp(rhs);
            std::swap(*this, tmp);
        }
        return *this;
    }

    // move assignment operator
    Thing& operator=(Thing&& rhs) noexcept {
        cout << "move operator\n";
        std::swap(_data, rhs._data);
        return *this;
    }


    // destructor necessary since we are working in dangerous new/delete territory
    ~Thing() noexcept {
        cout << "destructor " << (_data ? "object has data" : "object is empty") << "\n";

        delete[] _data;
    }
private:
    int* _data;
};
int main()
{
    cout << "constructing a\n";
    Thing a;

    cout << "constructing b with copy of a\n";
    Thing b(a);

    cout << "moving a to newly constructed c\n";
    Thing c(std::move(a));

    cout << "moving c back to a\n";
    a = std::move(c);

    cout << "create a new d\n";
    Thing d;
    cout << "replace d with a copy of a\n";
    d = a;

    return 0;
}

Program's output:

constructing a
default construct
constructing b with copy of a
copy constructor
moving a to newly constructed c
move constructor
moving c back to a
move operator
create a new d
default construct
replace d with a copy of a
copy operator
copy constructor
move constructor
move operator
move operator
destructor object is empty
destructor object has data
destructor object has data
destructor object is empty
destructor object has data
destructor object has data
like image 99
Richard Hodges Avatar answered Feb 16 '23 02:02

Richard Hodges