Есть ли способ, чтобы написать копию-конструктор для класса (скажем, Copyable
, который содержит std::unique_ptr
к Base
классу (но на самом деле хранят Derived
объектов.Возможно ли иметь экземпляр конструктивного класса, который содержит std :: unique_ptr <Base>, избегая срезания без основания, раскрывающего функцию «клонировать»?
Быстрого тест показывает ожидаемое разрезание происходит, потому что Copyable
не знает реального типа он держит Так что я полагаю, нужен метод clone
, но мне интересно, если есть способ, чтобы компилятор справиться с этим в какой-то лучший способ
код нарезки:.?
#include <algorithm>
#include <iostream>
#include <memory>
struct Base
{
Base(int i = 0) : i(i) {}
virtual ~Base() = default;
int i;
virtual int f() { return i; }
};
struct Derived : Base
{
Derived() = default;
virtual int f() override { return 42; }
};
struct Copyable
{
Copyable(std::unique_ptr<Base>&& base) : data(std::move(base)) {}
Copyable(const Copyable& other)
{
data = std::make_unique<Base>(*other.data);
}
std::unique_ptr<Base> data;
};
int main()
{
Copyable c(std::make_unique<Derived>());
Copyable c_copy = c;
std::cout << c_copy.data->f() << '\n';
}
clone
код:
#include <algorithm>
#include <iostream>
#include <memory>
struct Base
{
Base(int i = 0) : i(i) {}
virtual ~Base() = default;
int i;
virtual int f() { return i; }
virtual Base* clone() { return new Base(i); }
};
struct Derived : Base
{
Derived() = default;
virtual int f() override { return 42; }
virtual Derived* clone() override { return new Derived(); }
};
struct Copyable
{
Copyable(std::unique_ptr<Base>&& base) : data(std::move(base)) {}
Copyable(const Copyable& other)
{
data.reset(other.data->clone());
}
std::unique_ptr<Base> data;
};
int main()
{
Copyable c(std::make_unique<Derived>());
Copyable c_copy = c;
std::cout << c_copy.data->f() << '\n';
}
Очевидно, что код работает клон. Дело в том, что есть некоторые вещи, которые я бы хотел избежать:
new
.- случайная функция, которая должна быть частью интерфейса.
- Эта функция возвращает необработанный указатель.
- Каждый пользователь этого класса, который хочет быть переписанным, должен вызвать эту функцию.
Итак, есть ли «чистая» альтернатива?
Примечание. Я хочу использовать интеллектуальные указатели по всем очевидным причинам, мне просто нужно глубокое копирование std::unique_ptr
. Что-то вроде std::copyable_unique_ptr
, сочетая необязательную семантику перемещения с конструктором глубоких копирующих копий. Это самый чистый путь? Или это только добавляет путаницу?
«std :: copyable_unique_ptr» не имеет смысла. Наличие 'std :: unique_ptr', указывающее на клонируемый объект, имеет немного больше смысла. То, что у вас есть, - это, пожалуй, лучший способ. Есть несколько моментов: Конструктор-копир - это конструктор, поэтому он может иметь список инициализации члена. Функции 'clone' должны вызывать экземпляр-копир, копируя' * this'. И функция clone в производном классе должна возвращать указатель на базовый класс. –
@Someprogrammerdude Как 'copyable_unique_ptr' не имеет никакого смысла? Существует много сценариев, в которых вы оба хотите перемещать и копировать данные, каждый, когда они вам понадобятся. Конечно, вам нужно быть осторожным с 'std :: move', но невозможность копирования типа только для перемещения, не загрязняя интерфейс, кажется, подразумевает, что это разумное решение. – rubenvb
@rubenvb Опасности конструкторов копий, вызываемых, когда пользователь не ожидает его, является большой частью того, почему 'auto_ptr' устарел. Эффект будет немного менее плохим с помощью 'copyable_unique_ptr', но все равно будет плохо. «Clonable_unique_ptr» (или «clone_ptr», как в ответе Deduplicator), который не предоставляет конструктор копирования, но предоставляет функцию 'clone()' member, достигает того, чего вы надеялись достичь с помощью 'copyable_unique_ptr', но без опасности , – hvd