146

How do I implement a copy constructor for a class that has a unique_ptr member variable? I am only considering C++11.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
codefx
  • 9,872
  • 16
  • 53
  • 81
  • 9
    Well, what do you want the copy constructor to do? – Nicol Bolas Apr 16 '13 at 06:23
  • I read that unique_ptr is uncopyable. This makes me wonder how do use a class that has a unique_ptr member variable in a `std::vector`. – codefx Apr 16 '13 at 06:28
  • I think after copy if we have 2 objects with unique pointers pointing to same location(object) then does that make sense? They should be unique. If a class with pointer implements copy constructor then that pointer should be shared pointer, I believe not an unique pointer. – Abhijit-K Apr 16 '13 at 06:38
  • 5
    @AbhijitKadam You can make a deep copy of the content of the unique_ptr. In fact, that's often the sensible thing to do. – Cubic Apr 16 '13 at 11:24
  • Yes that makes sense. Saw the soln by Daniel. – Abhijit-K Apr 16 '13 at 11:59
  • 2
    Please note that you are possibly asking the wrong question. You probably do not want a copy constructor for your class containing a `unique_ptr`, you probably want a move constructor, if your goal is to put the data in a `std::vector`. On the other hand, the C++11 standard has automatically created move constructors, so maybe you do want a copy constructor... – Yakk - Adam Nevraumont Apr 16 '13 at 18:37
  • 3
    @codefx vector elements don't have to be copyable ; it just means that the vector won't be copyable. – M.M Jun 03 '15 at 22:31

7 Answers7

114

Since the unique_ptr can not be shared, you need to either deep-copy its content or convert the unique_ptr to a shared_ptr.

class A
{
   std::unique_ptr< int > up_;

public:
   A( int i ) : up_( new int( i ) ) {}
   A( const A& a ) : up_( new int( *a.up_ ) ) {}
};

int main()
{
   A a( 42 );
   A b = a;
}

You can, as NPE mentioned, use a move-ctor instead of a copy-ctor but that would result in different semantics of your class. A move-ctor would need to make the member as moveable explicitly via std::move:

A( A&& a ) : up_( std::move( a.up_ ) ) {}

Having a complete set of the necessary operators also leads to

A& operator=( const A& a )
{
   up_.reset( new int( *a.up_ ) );
   return *this,
}

A& operator=( A&& a )
{
   up_ = std::move( a.up_ );
   return *this,
}

If you want to use your class in a std::vector, you basically have to decide if the vector shall be the unique owner of an object, in which case it would be sufficient to make the class moveable, but not copyable. If you leave out the copy-ctor and copy-assignment, the compiler will guide your way on how to use a std::vector with move-only types.

András Aszódi
  • 8,948
  • 5
  • 48
  • 51
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 4
    Might be worth mentioning move constructors? – NPE Apr 16 '13 at 06:25
  • @Daniel, isn't it the case that for C++11, I need to follow the rule of four meaning implement both copy-ctor and move-ctor? – codefx Apr 16 '13 at 06:31
  • 4
    +1, but the move constructor should be emphasized even more. In a comment, the OP says the goal is to use the object in a vector. For that, move construction and move assignment are the only things required. – jogojapan Apr 16 '13 at 06:31
  • @codefx No. `std::unique_ptr` itself is an example of a class that enables moving, but not copying. – jogojapan Apr 16 '13 at 06:32
  • If a class (does not have any `unique_ptr` member variable) has all 4 copy/move-ctor, copy/move-assignment, when is move-assignment used? My understanding was that move-ctor will be used for rvalues. – codefx Apr 16 '13 at 06:38
  • @codefx Yes, for rvalues. Examples of how to get rvalues are found in the accepted answer to this question: http://stackoverflow.com/questions/13125632/when-does-move-constructor-get-called – jogojapan Apr 16 '13 at 06:43
  • Is there a resource leak in either of the copy/move assignment code snippet? – codefx Apr 16 '13 at 06:57
  • 43
    As a warning, the above strategy works for simple types like `int`. If you have a `unique_ptr` that stores a `Derived`, the above will slice. – Yakk - Adam Nevraumont Apr 16 '13 at 18:35
  • @Yakk and what kind of solution is applicable here ? – bahti May 18 '14 at 00:00
  • @bahti I might roll a `value_ptr` that uses a traits class to learn how to duplicate. – Yakk - Adam Nevraumont May 18 '14 at 01:19
  • 8
    There's no check for null, so as-is this allows a nullptr dereference. How about `A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}` – Ryan Haining Dec 06 '14 at 02:33
  • In the small example provided, null can't happen, but it's worth mentioning for in general. – Ryan Haining Dec 06 '14 at 02:39
  • @RyanHaining In the general case you are correct. Of course, the `unique_ptr` is a private member in the above example (and in most real-world use-cases) and it depends on the class if an empty `unique_ptr` is valid or not. – Daniel Frey Dec 06 '14 at 07:02
  • @Yakk, the issue of the slicing of copies reminds me of the issue of deleters. A correctly-initialized `unique_ptr` needs to have the correct deleter type, and I guess this means something like `std::unique_ptr>`. Might it be possible to extract the derived type from the deleter type, and therefore build a safer deep-copy? – Aaron McDaid Jul 28 '15 at 11:35
  • 1
    @Aaron in polymorphic situations, the deleter will be type erased somehow, or pointless (if you know the type to delete, why change only the deleter?). In any case, yes, this is the design of a `value_ptr` -- `unique_ptr` plus deleter/copier information. – Yakk - Adam Nevraumont Jul 28 '15 at 11:47
  • (...continued from my comment 15 minutes ago) @Yakk, I've just noticed that there are constructors of `unique_ptr` that do take deleters - so they must be stored somewhere? Constructors (3) and (4) [here](http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr) – Aaron McDaid Jul 28 '15 at 11:48
  • 1
    @AaronMcDaid yes, you can pass in a stateful deleter. The type of the `unique_ptr`'s deleter must be stateful, and in that case those constructors construct that deleter with state. The default deleter has no state, and there is nothing to pass in for those two constructors (well, other than `{}`). So you can create a stateful deleter for `unique_ptr`, but it won't be `std::default_delete` (which has no state). Stateless deleters are probably stored via the empty base optimization (assuming decent QOI). – Yakk - Adam Nevraumont Jul 28 '15 at 13:44
  • Can `A(A&& a)` and `A& operator=(A&& a)` simply be `default` here? – Nykakin Aug 08 '17 at 11:26
  • @Nykakin Yes, I think so. – Daniel Frey Aug 08 '17 at 14:37
  • Caution with converting `unique_ptr` to `shared_ptr` to make your class copyable. You will almost certainly end up with a class that has broken semantics. – user26756 Oct 18 '18 at 08:04
81

The usual case for one to have a unique_ptr in a class is to be able to use inheritance (otherwise a plain object would often do as well, see RAII). For this case, there is no appropriate answer in this thread up to now.

So, here is the starting point:

struct Base
{
    //some stuff
};

struct Derived : public Base
{
    //some stuff
};

struct Foo
{
    std::unique_ptr<Base> ptr;  //points to Derived or some other derived class
};

... and the goal is, as said, to make Foo copiable.

For this, one needs to do a deep copy of the contained pointer to ensure the derived class is copied correctly.

This can be accomplished by adding the following code:

struct Base
{
    //some stuff

    auto clone() const { return std::unique_ptr<Base>(clone_impl()); }
protected:
    virtual Base* clone_impl() const = 0;
};

struct Derived : public Base
{
    //some stuff

protected:
    virtual Derived* clone_impl() const override { return new Derived(*this); };                                                 
};

struct Foo
{
    std::unique_ptr<Base> ptr;  //points to Derived or some other derived class

    //rule of five
    ~Foo() = default;
    Foo(Foo const& other) : ptr(other.ptr->clone()) {}
    Foo(Foo && other) = default;
    Foo& operator=(Foo const& other) { ptr = other.ptr->clone(); return *this; }
    Foo& operator=(Foo && other) = default;
};

There are basically two things going on here:

  • The first is the addition of a user-defined copy constructor of Foo, This is necessary, as the unique_ptr-member iself has no copy constructor. In the declared copy-constructor, a new unique_ptr is created, and the pointer is set to a copy of the original pointee.

  • In case inheritance is involved, the copy of the original pointee must be done carefully. The reason is that doing a simple copy via std::unique_ptr<Base>(*ptr) in the code above would result in slicing, i.e., only the base component of the object gets copied, while the derived part is missing.

    To avoid this, the copy has to be done via the clone-pattern. The idea is to do the copy through a virtual function clone_impl() which returns a Base* in the base class. In the derived class, however, it is extended via covariance to return a Derived*, and this pointer points to a newly created copy of the derived class. The base class can then access this new object via the base class pointer Base*, wrap it into a unique_ptr, and return it via the actual clone() function which is called from the outside.

  • Second, by declaring a user-defined copy-constructor as done above, the move constructor gets deleted by the corresponding C++ language rules. The declaration via Foo(Foo &&) = default is thus just to let the compiler know that the standard move constructor still applies.

davidhigh
  • 14,652
  • 2
  • 44
  • 75
  • 4
    This should have been the accepted answer. Everyone else is going in circles in this thread, without hinting on why one would wish to copy an object pointed to by `unique_ptr` when direct containment would do otherwise. The answer??? **Inheritance**. – Tanveer Badar May 13 '18 at 04:33
  • 5
    One may be using unique_ptr even when they know the concrete type being pointed to for a variety of reasons: 1. It needs to be nullable. 2. The pointee is very large and we might have limited stack space. Often (1) and (2) will go together, hence one might on occasion prefer `unique_ptr` over `optional` for nullable types. – Ponkadoodle Jun 12 '18 at 22:54
  • 4
    The pimple idiom is another reason. – emsr Dec 08 '18 at 22:11
  • What if a base class should not be abstract? Leaving it without pure-specifier can lead to run-time bugs if you forget reimplement it in derived. – Oleksa Jul 03 '20 at 05:10
  • 1
    @OleksijPlotnyc'kyj: yes, if you implement the `clone_impl` in base, the compiler won't tell you if you forget it in the derived class. You could, however, use another base class `Cloneable` and implement a pure virtual `clone_impl` there. Then the compiler will complain if you forget it in the derived class. – davidhigh Jul 03 '20 at 13:37
  • You can also override the clone function directly, without slicing or implementing clone_impl. Explicitly state the return type std::unique_ptr and return std::make_unique(*this) in Base and std::make_unique(*this) ) in Derived. see Tyler D.'s post https://stackoverflow.com/questions/64060101/copy-constructor-of-a-class-containing-a-smart-pointer – Daniel Ford Oct 06 '21 at 05:53
  • @DanielFord: yes, you could, but then you could never clone a Derived. Think about multiple levels in an inheritance hierachy, for instance. – davidhigh Oct 06 '21 at 10:45
17

Try this helper to create deep copies, and cope when the source unique_ptr is null.

    template< class T >
    std::unique_ptr<T> copy_unique(const std::unique_ptr<T>& source)
    {
        return source ? std::make_unique<T>(*source) : nullptr;
    }

Eg:

class My
{
    My( const My& rhs )
        : member( copy_unique(rhs.member) )
    {
    }

    // ... other methods

private:
    std::unique_ptr<SomeType> member;
};
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • 2
    Will it correctly copy if source points to something derived from T? – Roman Shapovalov Apr 13 '16 at 16:00
  • 4
    @RomanShapovalov No, probably not, you would get slicing. In that case, the solution would probably be to add a virtual unique_ptr clone() method to your T type, and provide overrides of the clone() method in types derived from T. The clone method would make a new instance of the derived type and return that. – Scott Langham Apr 13 '16 at 22:14
  • Are there no unique/scoped pointers in c++ or boost libraries that has the deep copy functionality built in? It would be nice to not have to create our custom copy constructors etc. for classes that uses these smart pointers, when we want the deep copy behavior, which is often the case. Just wondering. – shadow_map Jul 27 '16 at 05:35
7

Daniel Frey mention about copy solution,I would talk about how to move the unique_ptr

#include <memory>
class A
{
  public:
    A() : a_(new int(33)) {}

    A(A &&data) : a_(std::move(data.a_))
    {
    }

    A& operator=(A &&data)
    {
      a_ = std::move(data.a_);
      return *this;
    }

  private:
    std::unique_ptr<int> a_;
};

They are called move constructor and move assignment

you could use them like this

int main()
{
  A a;
  A b(std::move(a)); //this will call move constructor, transfer the resource of a to b

  A c;
  a = std::move(c); //this will call move assignment, transfer the resource of c to a

}

You need to wrap a and c by std::move because they have a name std::move is telling the compiler to transform the value to rvalue reference whatever the parameters are In technical sense, std::move is analogy to something like "std::rvalue"

After moving, the resource of the unique_ptr is transfer to another unique_ptr

There are many topics that document rvalue reference; this is a pretty easy one to begin with.

Edit :

The moved object shall remain valid but unspecified state.

C++ primer 5, ch13 also give a very good explanation about how to "move" the object

Community
  • 1
  • 1
StereoMatching
  • 4,971
  • 6
  • 38
  • 70
  • 1
    so what happens to object `a` after calling std::move(a) in the `b` move constructor? Is it just totally invalid? – David Doria Mar 24 '16 at 18:28
5

I suggest use make_unique

class A
{
   std::unique_ptr< int > up_;

public:
   A( int i ) : up_(std::make_unique<int>(i)) {}
   A( const A& a ) : up_(std::make_unique<int>(*a.up_)) {};

int main()
{
   A a( 42 );
   A b = a;
}
Splash
  • 1,288
  • 2
  • 18
  • 36
0

unique_ptr is not copyable, it is only moveable.

This will directly affect Test, which is, in your second, example also only moveable and not copyable.

In fact, it is good that you use unique_ptr which protects you from a big mistake.

For example, the main issue with your first code is that the pointer is never deleted which is really, really bad. Say, you would fix this by:

class Test
{
    int* ptr; // writing this in one line is meh, not sure if even standard C++

    Test() : ptr(new int(10)) {}
    ~Test() {delete ptr;}
};

int main()
{       
     Test o;
     Test t = o;
}

This is also bad. What happens, if you copy Test? There will be two classes that have a pointer that points to the same address.

When one Test is destroyed, it will also destroy the pointer. When your second Test is destroyed, it will try to remove the memory behind the pointer, as well. But it has already been deleted and we will get some bad memory access runtime error (or undefined behavior if we are unlucky).

So, the right way is to either implement copy constructor and copy assignment operator, so that the behavior is clear and we can create a copy.

unique_ptr is way ahead of us here. It has the semantic meaning: "I am unique, so you cannot just copy me." So, it prevents us from the mistake of now implementing the operators at hand.

You can define copy constructor and copy assignment operator for special behavior and your code will work. But you are, rightfully so (!), forced to do that.

The moral of the story: always use unique_ptr in these kind of situations.

IceFire
  • 4,016
  • 2
  • 31
  • 51
0

As davdihigh mentioned in his answer. One uses a unique_ptr mostly because of inheritance. Thus, most of the time you have a std::unique_ptr<BaseType> instead of std::unique_ptr<ChildType> (where class ChildType : public BaseType {}). However, in order to create a std::unique_ptr<BaseType>, the ChildType is always directly involved at some point. You either pass a pointer to the ChildType, or a unique_ptr<ChildType> into the constructor.

There are situations where it's easier to switch the used unique_ptr class, than add a clone interface to all the Base- and ChildTypes that will be used at some point.

This enables one to write a wrapper around unique_ptr with a clone method (similar to the already existing custom deleter), like so:

template<typename T>
requires (std::has_virtual_destructor_v<T>)
class clonable_unique_ptr {

public: // Associated types
    using pointer = std::unique_ptr<T>::pointer;
    using element_type = std::unique_ptr<T>::element_type;
    using cloner_type = std::function<T*(const T*)>;

private:
    std::unique_ptr<T> ptr;
    cloner_type cloneFn = [](const T*) { return nullptr; };

    clonable_unique_ptr(T* rawPtr, cloner_type cloneFn)
        : ptr(rawPtr), cloneFn(cloneFn) {}

public:
    constexpr clonable_unique_ptr() noexcept {}
    constexpr clonable_unique_ptr(std::nullptr_t) noexcept {}

    /**
     * ctor creating a clonable_unique_ptr from a given rawPtr.
     * @attention The rawPtr must be of the type that it actually points to!
     *            Meaning: The ChildType
     */
    template<typename U>
    explicit clonable_unique_ptr(U* rawPtr) : ptr(rawPtr) {
        static_assert(std::derived_from<U, T>, "Types not compatible");
        static_assert(!std::is_abstract_v<U>, "Entrypoint ctors need to be given the actual type, not the base type!");
        static_assert(std::is_copy_constructible_v<U>, "Type needs to be copy constructible");
        cloneFn = [](const T* ptr) { return new U(*static_cast<const U*>(ptr)); };
    }

    /**
     * ctor creating a clonable_unique_ptr from a given unique_ptr<ChildType>.
     * @attention The ptr must be of the type that it actually points to!
     *            Meaning: The ChildType
     */
    template<typename U>
    clonable_unique_ptr(std::unique_ptr<U>&& o) {
        static_assert(std::derived_from<U, T>, "Types not compatible");
        static_assert(!std::is_abstract_v<U>, "Entrypoint ctors need to be given the actual type, not the base type!");
        static_assert(std::is_copy_constructible_v<U>, "Type needs to be copy constructible");
        cloneFn = [](const T* ptr) { return new U(*static_cast<const U*>(ptr)); };
        ptr = std::move(o);
    }

    /**
     * move ctor, moving from another instance of clonable_unique_ptr
     */
    clonable_unique_ptr(clonable_unique_ptr&& o)
        : ptr(std::move(o.ptr)), cloneFn(std::move(o.cloneFn)) {}

    /** move assignable */
    clonable_unique_ptr& operator=(clonable_unique_ptr&&) = default;


    // default unique_ptr interface with accessors
    T* get() const noexcept { return ptr.get(); }
    T* operator->() { return ptr.get(); }
    auto& operator*() const noexcept(noexcept(*std::declval<pointer>())) {
        return *ptr;
    }
    void reset() noexcept {
        ptr.reset();
        cloneFn = [](const T*) { return nullptr; };
    }
    void swap(clonable_unique_ptr& o) noexcept {
        ptr.swap(o.ptr);
        std::swap(cloneFn, o.cloneFn);
    }

    /**
     * Clone the instance of T contained in this pointer by using the copy ctor
     * of the ChildType that is contained in this ptr.
     */
    clonable_unique_ptr<T> clone() const {
        return clonable_unique_ptr(cloneFn(get()), cloneFn);
    }
};


template<typename T0, typename T1>
bool inline operator==(const clonable_unique_ptr<T0>& ptr0, const clonable_unique_ptr<T1>& ptr1) {
    return ptr0.get() == ptr1.get();
}
template<typename T0>
bool inline operator==(const clonable_unique_ptr<T0>& ptr0, std::nullptr_t) {
    return ptr0.get() == nullptr;
}

Like this, the responsibility of implementing the clone was pushed into the pointer and out of BaseClass/ChildClass. Some examples:

struct BaseClass {
    virtual void doStuff() = 0;
    virtual ~BaseClass() {}
};

struct ChildClass : public BaseClass {
    std::string value;
    ChildClass(std::string value) : value(value) {}
    void doStuff() override {
        std::cout << "doStuff: " << value << " " << this << std::endl;
    }
};

The clonable_unique_ptr can then be created using either a pointer to ChildType:

clonable_unique_ptr<BaseClass> fromPtr(new ChildClass("cake"));
clonable_unique_ptr<BaseClass> fromPtrClone = fromPtr.clone();
fromPtr->doStuff(); // two different instances
fromPtrClone->doStuff();

or from a std::unique_ptr<ChildType> for easier compatibility:

clonable_unique_ptr<BaseClass> fromUPtr = std::make_unique<ChildClass>("blub");
clonable_unique_ptr<BaseClass> fromUPtrClone = fromUPtr.clone();
fromUPtr->doStuff(); // two different instances
fromUPtrClone->doStuff();

Here is a link to Godbolt to toy with it. I didn't add custom deleter support to this wrapper yet, because I personally don't need it, but that should be trivial.

Markus Ebner
  • 148
  • 9