Вызов оператора присваивания родительского перемещения, когда исходный объект все еще необходимо использовать после - PullRequest
1 голос
/ 30 мая 2020

TL; DR - не стесняйтесь пометить это как дубликат, если это так; Я удалю вопрос. Однако я не смог ничего найти, немного посмотрев вокруг.

Рассмотрим следующие классы:

class Base
{
public:
    Base(std::string var1);

    Base& operator=(Base&& other) noexcept
    {
        if (this != &other)
            var1_ = std::move(other.var1_);
        return *this;
    }

protected:
    std::string var1_;
};

class Child final : public Base
{
public:
    Child(std::string var1, std::string var2);

    Child& operator=(Child&& other) noexcept
    {
        if (this != &other)
        {
            // Take note of HERE for explanation below
            Base::operator=(std::move(other));
            var2_ = std::move(other.var2_);
        }
    }

private:
    std::string var2_;
};

Здесь есть два класса, Child производные от Base. Child имеет больше участников, чем Base. В операторе присваивания перемещения Child у нас есть ситуация, когда у нас есть дополнительные члены, которые также необходимо присвоить значениям членов other. Однако сначала мы перемещаем память other в родительский оператор присваивания перемещения, так разве эти данные не станут непригодными для использования?

Полагаю, я просто запутался в (1), если что-то не так с тем, что у меня выше, (2) если да, то почему это работает, поскольку other должен быть перемещен, прежде чем он и (3) если нет, то как лучше всего выполнить sh задачу, которая у меня есть?

Лучше всего просто инициализировать элементы Child first и затем передать его оператору присваивания Base move? Есть ли разница?

1 Ответ

6 голосов
/ 30 мая 2020

Да, это безопасно.

Во-первых, обратите внимание, что правило об отказе от использования перемещенного объекта является руководством по умолчанию для проектирования и использования объектов. Это не правило самого языка. Это обычная практика для объектов класса обычно после их перемещения, включая типы классов стандартной библиотеки, которые не указывают иное (например, std::unique_ptr<T> гарантирует, что указатель перемещенного объекта содержит нуль указатель).

Итак, поскольку вы точно знаете, что Base::operator= делает с other, и это перемещается только от other.var1_, все еще безопасно использовать other.var2_.

Есть еще один причина того, что этот шаблон безопасен, даже если вы точно не знаете, что делает Base::operator=, или хотите написать код Child, чтобы продолжать работать, даже если детали Base изменятся. Объект Child содержит два подобъекта: подобъект базового класса Base и подобъект члена var2_. Эти объекты по своей сути ничего не знают друг о друге. (Они могли бы вручную настроить взаимодействие друг с другом с помощью указателей, виртуальных функций и т. Д. c., Чтобы усложнить ситуацию, но такого рода вещи должны быть задокументированы для Base или известны в Child). Оператор * перемещается только из подобъекта базового класса, а не из подобъекта члена, поэтому подобъект члена не затрагивается, и определение Child::operator=(Child&&) безопасно от изменений в Base.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...