Явный вызов конструктора копирования объекта в unique_ptr

Я использую идиому pimpl с const std::unique_ptr провести реализацию класса. Мой класс должен поддерживать конструкцию копирования и назначение копирования. То, что я хотел бы сделать, это вручную вызвать конструктор копирования impl класс внутри unique_ptr, Однако я не вижу, как.

#include <memory>

struct potato {
    potato();
    ~potato();
    potato(const potato& p);

private:
    struct impl;
    const std::unique_ptr<impl> _pimpl;
};

struct potato::impl {
    int carbs = 42;
};

potato::potato()
        : _pimpl(std::make_unique<impl>()) {
}

potato::~potato() = default;

potato::potato(const potato& p) {
    // Try to call the copy constructor of impl, stored in unique_ptr, not the
    // unique_ptr copy-constructor (which doesn't exist).
    _pimpl.get()->impl(p._pimpl); // This doesn't work.
}

Я проверил еще один вопрос о явном вызове конструктора копирования для объекта. Один ответ рекомендуется с использованием нового размещения.

Object dstObject;
new(&dstObject) Object(&anotherObject);

Могу ли я использовать это в моем конструкторе копирования? Если так, то как? Я не очень понимаю, что там происходит. Спасибо.

2 ответа

Решение

То, что я хотел бы сделать, это вручную вызвать конструктор копирования impl класс внутри unique_ptr

Здесь кроется ваша ошибка. Поскольку вы находитесь внутри (копирования) конструктора potato, нет никаких impl уже созданный объект, над которым вам придется "вручную" вызывать конструктор копирования.

Просто создай свой новый impl передавая ему ссылку на оригинал impl копировать.

potato::potato(const potato& p)
    : _pimpl(std::make_unique<impl>(*p._pimpl) {
}

Что касается назначения, вы можете легко переслать на impl оператор присваивания:

potato &operator=(const potato &p) {
    *_pimpl = *p._pimpl;
    return *this;
}

Вы можете явно вызывать конструкторы в неинициализированном хранилище с размещением new оператор, как вы упомянули. Вы можете вернуть хранилище объекта в неинициализированное состояние, явно вызвав его деструктор.

Вот простая реализация оператора присваивания, который явно вызывает конструктор копирования и деструктор, который вы уже определили как часть вашего интерфейса:

#include <new>

potato& potato::operator=(const potato& x)
{
  if ( this != &x ) { // check for self-assignment!
    this->~potato();
    new(this) potato(x);
  }

  return *this;
}

Возможно, вы также захотите определить конструктор перемещения и перегрузить оператор присваивания, когда правая часть является временной. Перегруз для potato&& src так же как const potato& src, Вы можете использовать идиому подкачки, если ваш класс поддерживает его, или тот же код, что и выше, но вызов new(this) potato(std::move(src));,

Если у вас есть доступ к деструктору и конструктору копирования класса, заключенному в умный указатель, вы можете сделать с ними тот же трюк, просто разыменовав умные указатели. Вы, вероятно, не хотите, хотя.

Конструктор копирования по умолчанию и оператор присваивания должны работать нормально, если содержимое класса - это интеллектуальные указатели, контейнеры STL и т. Д. Возможно, вы захотите скопировать данные, на которые ссылаются умные указатели, написав *p = x или же *p = *q или же std::swap, а не явные вызовы конструктора копирования.

Другие вопросы по тегам