Search code examples
c++c++11clonecopy-constructorunique-ptr

Auto-cloning unique_ptr


std::unique_ptr has a deleted copy constructor, which means that if you have a unique_ptr in your class Foo as a data member then you must write your own copy constructor for Foo and manually deep-copy that member (even if the compiler-generated copy constructor would be fine for all other members).

In order to be able to copy in a polymorphic way, the clone() method pattern can be used. Let's assume our objects have a clone method like this:

class Base {
    virtual std::unique_ptr<Base> clone() = 0;
};

Foo looks like this now:

class Foo {
public:
    ...
    Foo(Foo const& other)
        : b(other.b->clone())
        , // init 10 more members that could otherwise be auto-copied just fine
          // with the automatically generated copy constructor
    {}
    ...

private:
    std::unique_ptr<Base> b;
    //10 more data members

};

Now, I found a way to auto-clone Foo::b, by writing a wrapper over unique_ptr that defines the copy constructor and assignment by calling clone.

template <typename T>
class auto_cloned_unique_ptr
{
private:
    std::unique_ptr<T> up;

public:
    // copy constructor
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T> const& other)
        : up(other.up->clone()) {}

    // copy assignment
    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T> const& other)
    {
        this->up = other.up->clone();
        return *this;
    }

    auto_cloned_unique_ptr(std::unique_ptr<T> _up)
        : up(std::move(_up)) {}

    // Delegate everything else to unique_ptr
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T>&& other)
        : up(std::move(other.up)) {}

    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T>&& other)
    {
        this->up = std::move(other.up);
        return *this;
    }

    auto operator *() const {return *up;}
    auto operator->() const {return up.operator->();}
    auto get() -> const {return up.get();}

};

Now if we use this we don't need to define our own copy constructor:

class Foo2 {
public:
    ...

private:
    auto_cloned_unique_ptr<Base> b;
    //10 more data members

};

Is such an approach very much frowned upon (for using a non-standard wrapper over unique_ptr)?


Solution

  • Let me first paraphrase what you want to do: You want that each instance of Foo has its own instance of Base in b; in particular, if you copy a Foo, the copy will have its own new Base, initially with the same "value". In other words, Base should behave like a value.

    At the same time, you can't store Base directly in Foo because it is an abstract class. In other words, you want b to be polymorphic.

    There you have it: you want a polymorphic value. Other people have recognized this need and proposed for C++20 as polymorphic_value<Base>. From the documentation:

    The class template, polymorphic_value, confers value-like semantics on a free-store allocated object. A polymorphic_value may hold an object of a class publicly derived from T, and copying the polymorphic_value will copy the object of the derived type.

    It has a reference implementation that you can use as of now. Very simply put, it is an wrapper around std::unique_ptr similar to what you propose.