1

Есть ли способ, чтобы написать копию-конструктор для класса (скажем, 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'; 
} 

Очевидно, что код работает клон. Дело в том, что есть некоторые вещи, которые я бы хотел избежать:

  1. new.
  2. случайная функция, которая должна быть частью интерфейса.
  3. Эта функция возвращает необработанный указатель.
  4. Каждый пользователь этого класса, который хочет быть переписанным, должен вызвать эту функцию.

Итак, есть ли «чистая» альтернатива?

Примечание. Я хочу использовать интеллектуальные указатели по всем очевидным причинам, мне просто нужно глубокое копирование std::unique_ptr. Что-то вроде std::copyable_unique_ptr, сочетая необязательную семантику перемещения с конструктором глубоких копирующих копий. Это самый чистый путь? Или это только добавляет путаницу?

+0

«std :: copyable_unique_ptr» не имеет смысла. Наличие 'std :: unique_ptr', указывающее на клонируемый объект, имеет немного больше смысла. То, что у вас есть, - это, пожалуй, лучший способ. Есть несколько моментов: Конструктор-копир - это конструктор, поэтому он может иметь список инициализации члена. Функции 'clone' должны вызывать экземпляр-копир, копируя' * this'. И функция clone в производном классе должна возвращать указатель на базовый класс. –

+0

@Someprogrammerdude Как 'copyable_unique_ptr' не имеет никакого смысла? Существует много сценариев, в которых вы оба хотите перемещать и копировать данные, каждый, когда они вам понадобятся. Конечно, вам нужно быть осторожным с 'std :: move', но невозможность копирования типа только для перемещения, не загрязняя интерфейс, кажется, подразумевает, что это разумное решение. – rubenvb

+1

@rubenvb Опасности конструкторов копий, вызываемых, когда пользователь не ожидает его, является большой частью того, почему 'auto_ptr' устарел. Эффект будет немного менее плохим с помощью 'copyable_unique_ptr', но все равно будет плохо. «Clonable_unique_ptr» (или «clone_ptr», как в ответе Deduplicator), который не предоставляет конструктор копирования, но предоставляет функцию 'clone()' member, достигает того, чего вы надеялись достичь с помощью 'copyable_unique_ptr', но без опасности , – hvd

ответ

4

Вы можете создать класс clone_ptr для любого объекта, который вы знаете, статически, как клонировать.

Он будет содержать указатель на объект и указатель на функцию клонирования указанного объекта, возможно, из преобразования безстоящей лямбда.