Как объяснено в этом ответе, идиома копирования и свопинга реализована следующим образом:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
Имея значение параметра MyClass как параметра для оператора =, параметр может быть сконструирован либо конструктором копирования, либо конструктором перемещения. Затем вы можете безопасно извлечь данные из параметра. Это предотвращает дублирование кода и помогает в безопасности исключений.
В ответе вы можете либо поменять, либо переместить переменные во временное. В первую очередь речь идет о замене. Однако обмен, если он не оптимизирован компилятором, включает в себя три операции перемещения, а в более сложных случаях делает дополнительную дополнительную работу. Когда все, что вам нужно, - переместить временный объект в назначенный объект.
Рассмотрим этот более сложный пример, включающий шаблон наблюдателя . В этом примере я написал код оператора присваивания вручную. Акцент делается на конструкторе перемещения, операторе присваивания и методе свопинга:
class MyClass : Observable::IObserver
{
private:
std::shared_ptr<Observable> observable;
public:
MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
friend void swap(MyClass& first, MyClass& second)
{
//Checks for nullptr and same observable omitted
using std::swap;
swap(first.observable, second.observable);
second.observable->unregisterObserver(first);
first.observable->registerObserver(first);
first.observable->unregisterObserver(second);
second.observable->registerObserver(second);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
}
Ясно, что дублируемая часть кода в этом ручном письменном операторе присваивания совпадает с дублирующей конструкцией перемещения. Вы могли бы выполнить обмен в операторе присваивания, и поведение было бы правильным, но оно потенциально могло бы выполнять больше ходов и выполнять дополнительную регистрацию (в свопе) и отмену регистрации (в деструкторе).
Разве не было бы лишнего смысла повторно использовать код конструктора перемещения?
private:
void performMoveActions(MyClass&& other)
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
public:
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
performMoveActions(other);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
performMoveActions(other);
}
Мне кажется, что этот подход никогда не уступает подходу подкачки. Правильно ли я считаю, что идиома с копией и заменой будет лучше, чем идиома копирования и перемещения в С++ 11, или я пропустил что-то важное?