Swap-идиома абстрактного класса с закрытыми данными

Предположим, у нас есть эти два класса с реализованными идиомами подкачки. Конструктор копирования и оператор присваивания базового класса удаляются, так как это не имеет смысла. Однако swap-метод реализован, так как содержит член.

namespace std
{
template<>
void swap<Base>(Base& a, Base& b)
{
a.swap(b);
}

template<>
void swap<Derived>(Derived& a, Derived& b)
{
a.swap(b);
}
}

class Base
{
public:
Base(int ID) : ID_(ID) {}
virtual std::string getString()=0;

Base(const Base&)=delete;
operator=(const Base&)=delete;

void swap(Base& rhs)
{
std:swap(ID_, rhs.ID_);
}
private:
int ID_;
}

class Derived : public Base
{
public:
Derived(int ID, bool Value) : Base(ID), Value_(Value) {}
virtual ~Derived() {}
Derived(Derived& rhs)
{
std::swap(*this, rhs);
}

virtual std::string getString() {return Value_ ? "True" : "False"}
void swap(Derived& lhs, Derived& rhs)
{
std::swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs);
std::swap(lhs.Value_, rhs.Value_);
}
private:
bool Value_;
}

Как видно из многих примеров, это будет стандартный способ сделать это, я полагаю.
Тем не менее, я вижу проблему с общедоступным Base :: swap, так как нельзя менять местами только абстрактный базовый класс!

Не лучше ли удалить шаблон для базового класса и сделать защищенным метод Base :: swap:

class Base
{
...
protected:
void swap(Base& rhs, Base &lhs);
}

class Derived : public Base
{
...
public:
void swap(Derived& lhs, Derived& rhs)
{
Base::swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs);
std::swap(lhs.Value_, rhs.Value_);
}
}

Предполагая, что есть другой класс, производный от base, с первой реализацией можно было бы поменять местами ID, однако члены данных производных объектов остались бы прежними.

Итак, правильно ли я думаю, что обмен абстрактного класса не должен быть возможным извне?

0

Решение

Конструктор копирования и оператор присваивания базового класса удаляются, так как это не имеет смысла.

На самом деле, это ужасно. Теперь вы сделали Derived uncopyable! Зачем? Нет причин добавлять это ограничение. Конструктор копирования по умолчанию и оператор присваивания Base совершенно разумны в контексте копирования самого базового класса иерархии.

Как только вы отмените это, нет необходимости делать что-либо еще, как std::swap(derived1, derived2) уже сделал бы правильную вещь. Конструкция / операция перемещения по умолчанию верна. Всегда хорошо, если компилятор сделает все за вас.

Но если вы хотите переопределить swap во всяком случае, правильный способ сделать это будет как не являющийся членом друг:

class Base {
...
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.ID_, rhs.ID_);
}
};

class Derived : public Base {
...
friend void swap(Derived& lhs, Derived& rhs) {
using std::swap;
swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs));
swap(lhs.Value_, rhs.Value_);
}
};

Кроме того, ваш Derived Конструктор копирования не имеет смысла. Удалите это согласно первому абзацу моего ответа.

1

Другие решения