Я видел copy-and-swap идиому, рекомендованную в различных помещает как рекомендуемый/лучший/единственный способ реализовать сильную защиту исключений для оператора присваивания. Мне кажется, что этот подход также имеет недостаток.
Рассмотрим следующий упрощенный векторно-подобный класс, который использует copy-and-swap:
class IntVec {
size_t size;
int* vec;
public:
IntVec()
: size(0),
vec(0)
{}
IntVec(IntVec const& other)
: size(other.size),
vec(size? new int[size] : 0)
{
std::copy(other.vec, other.vec + size, vec);
}
void swap(IntVec& other) {
using std::swap;
swap(size, other.size);
swap(vec, other.vec);
}
IntVec& operator=(IntVec that) {
swap(that);
return *this;
}
//~IntVec() and other functions ...
}
Реализация задания с помощью конструктора копирования может быть эффективной и гарантировать безопасность исключений, но также может вызывать ненужное распределение, что потенциально может вызвать неправильную ошибку вне памяти.
Рассмотрим случай назначения 700 МБ IntVec
1GB IntVec
на машине с лимитом кучи < 2GB. Оптимальное назначение поймет, что у него уже достаточно выделенной памяти и только скопировать данные в уже выделенный буфер. Реализация copy-and-swap приведет к распределению другого 700-мегабайтного буфера перед выпуском 1 ГБ, в результате чего все 3 попытаются сосуществовать в памяти сразу, что вызовет ненужную ошибку.
Эта реализация решит проблему:
IntVec& operator=(IntVec const& that) {
if(that.size <= size) {
size = that.size;
std::copy(that.vec, that.vec + that.size, vec);
} else
swap(IntVec(that));
return *this;
}
Итак, нижняя строка:
Правильно ли это, что это проблема с идиомой "копирование и своп", или обычная оптимизация компилятора каким-то образом устраняет дополнительное распределение, или я упускаю из виду какую-то проблему с моей "лучшей" версией, которую решает "копировать и сменять", или я неправильно делаю математику/алгоритмы, и проблема на самом деле не существует?