Конструктор и оператор присваивания в дереве

Я написал рабочий копирующий конструктор, оператор копирования, и деструктор для своего дерева, но не могу разобраться с конструктором и оператором присваивания. Пробовал разные варианты, но каждый раз получаю одни и те же ошибки. Также у меня есть рабочая функция swap. Дерево содержит корень и переменную с количеством слов. В чем может быть проблема?

tree::tree(tree &&rhs)
{
    tree tmp(rhs);
    swap(tmp);
    tree * ptr = &tmp;
    ptr->~tree();
}

tree &tree::operator=(tree &&rhs) {
    tree tmp(rhs);
    swap(tmp);
    tree * ptr = &tmp;
    ptr->~tree();
    return *this;
}

void tree::swap(tree &rhs)
{
    std::swap(root, rhs.root);
    std::swap(size, rhs.size);
}

Ответы (1 шт):

Автор решения: HolyBlackCat

Во-первых, надо разобраться с терминами. Вы пишете "перемещающие" операции (перемещающий конструктор и перемещающий оператор присваивания). А версии с const tree &, которые у вас уже есть, это "копирующие" операции (копирующий конструктор и копирующий оператор присваивания).

Во-вторых, даже если убрать лишний вызов деструктора, это очень неудачная реализация, потому что вы копируете все дерево. А смысл перемещения как раз в избегании лишних копий. С тем же успехом можно просто не писать перемещающие операции, и компилятор будет сам использовать вместо них копирующие.

Самое минимальное исправление такое:

tree::tree(tree &&other) : root(other.root), size(other.size)
{
    other.root = nullptr;
    other.size = 0;
}

tree &tree::operator=(tree &&other)
{
    tree tmp(std::move(other));
    swap(*this, tmp); // Или убрать самодельный свап и здесь руками свапнуть поля.
    return *this;
}

Но лучше так:

tree(tree &&other) noexcept
    : root(std::exchange(other.root, {})), size(std::exchange(other.size, {}))
{}

tree &tree::operator=(tree other) noexcept
{
    swap(*this, other);
    return *this;
}

И убрать копирующее присваивание, потому что это (с параметром tree по значению) - универсальное, и работает и для копирования, и для присваивания.

std::exchange просто для красоты.

noexcept - потому что без него, если положить ваше дерево в стандартный контейнер вроде std::vector, он в некоторых ситуациях будет брезговать перемещением и вызывать копирование, чтобы избежать возможных исключений.

→ Ссылка