7

I have a class with one std::unique_ptr as class member. I was wondering, how to correctly define the copy constructor, since I'm getting the following compiler error message: error C2248: std::unique_ptr<_Ty>::unique_ptr : cannot access private member declared in class 'std::unique_ptr<_Ty>. My class design looks something like:

template <typename T>
class Foo{
    public:
        Foo(){};
        Foo( Bar<T> *, int );
        Foo( const Foo<T> & );
        ~Foo(){};

        void swap( Foo<T> & );
        Foo<T> operator = ( Foo<T> );

    private:
        std::unique_ptr<Bar> m_ptrBar;
        int m_Param1;

};

template < typename T >
Foo<T>::Foo( const Foo<T> & refFoo )
:m_ptrBar(refFoo.m_ptrBar), 
m_Param1(refFoo.m_Param1)
{
    // error here!
}

template < typename T >
void Foo<T>::swap( Foo<T> & refFoo ){
    using std::swap;
    swap(m_ptrBar, refFoo.m_ptrBar);
    swap(m_Param1, refFoo.m_Param1);
 }

 template < typename T >
 Foo<T> Foo<T>::operator = ( Foo<T> Elem ){
    Elem.swap(*this);
    return (*this);
 }
Tin
  • 1,006
  • 1
  • 15
  • 27

3 Answers3

6

Assuming the goal is to copy-construct the uniquely-owned Bar,

template < typename T >
Foo<T>::Foo( const Foo<T> & refFoo )
: m_ptrBar(refFoo.m_ptrBar ? new Bar(*refFoo.m_ptrBar) : nullptr),
  m_Param1(refFoo.m_Param1)
{
}
Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • @ Cubbi, thanks. I've now another problem. The `Bar` class is actually an abstract base class, and therefore I'm getting a new error message: `error C2259: 'Bar' : cannot instantiate abstract class`, Is there any solution, besides turning the abstract base class, to a simple base class? – Tin Nov 29 '11 at 19:04
  • 2
    @Tin: in that case, you'll need to add a pure virtual `clone()` function to the base class, overridden in each derived class to create a copy using `new`. Then the initialiser becomes `bar(foo.bar ? foo.bar->clone() : nullptr)`. – Mike Seymour Nov 29 '11 at 19:30
  • @Tin The C++FAQ calls that ["virtual constructor"](http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8) – Cubbi Nov 29 '11 at 19:40
  • @MikeSeymour @Cubbi if I've now a class with a private member: `std::vector > mvector;`, and would like to define the copy constructor & assignment operator, then I would need copy element by element calling the `'clone()' virtual function`, right? Because if I follow the traditional implementation for the copy-constructor in the parameterized list, i.e. `MyCollection::MyCollecyion(const refCol):myvector(refCol.myvector){}`, I get a compiler error. – Tin Nov 29 '11 at 20:36
  • @Cubbi, what if now I would just only like to `std::move` the pointer? I tried sth. like `m_ptrBar(std::move(refFoo.m_ptrBar))`, but didn't work out. Anny suggestions? – Tin Jan 14 '12 at 12:05
2

Unique_ptr documentation:

Stores a pointer to an owned object. The object is owned by no other unique_ptr. 
The object is destroyed when the unique_ptr is destroyed.

You cant copy it because two objects can't own it.

Try switching to a std::shared_ptr.

EDIT I should point out that this would make both objects have a pointer to that same object. If you want to copy the uniquely owned object Cubbi's solution is the correct one.

John Humphreys
  • 37,047
  • 37
  • 155
  • 255
  • @ w00te, thanks. One more question. What if the Bar class is actually an abstract base class? I'm getting a new error message: `error C2259: 'Bar' : cannot instantiate abstract class`. Is there any solution, besides turning the abstract base class, to a simple base class? – Tin Nov 29 '11 at 19:11
  • Cubbi's solution creates a new Bar object to be contained within the unique_ptr in the new class. If bar is abstract then that cannot work - it would have to create a new object of the applicable derived class instead. You'll have to add logic to achieve that. – John Humphreys Nov 29 '11 at 19:18
2

A possibility is to create a new clone_ptr type for this.

Below is a rudimentary example of a clone_ptr that invokes the correct copy constructor (and destructor) of a derived object. This is done here by creating a "type erasure" helper when the clone_ptr is created.

Other implementations may be found on the Internet.

#include <memory>

namespace clone_ptr_detail
{
template <class T>
class clone_ptr_helper_base
{
public:
    virtual ~clone_ptr_helper_base() {}
    virtual T* clone(const T* source) const = 0;
    virtual void destroy(const T* p) const = 0;
};

template <class T, class U>
class clone_ptr_helper: public clone_ptr_helper_base<T>
{
public:
    virtual T* clone(const T* source) const
    {
        return new U(static_cast<const U&>(*source));
    }
    virtual void destroy(const T* p) const
    {
        delete static_cast<const U*>(p);
    }
};
}

template <class T>
class clone_ptr
{
    T* ptr;
    std::shared_ptr<clone_ptr_detail::clone_ptr_helper_base<T>> ptr_helper;
public:
    template <class U>
    explicit clone_ptr(U* p): ptr(p), ptr_helper(new clone_ptr_detail::clone_ptr_helper<T, U>()) {}

    clone_ptr(const clone_ptr& other): ptr(other.ptr_helper->clone(other.ptr)), ptr_helper(other.ptr_helper) {}

    clone_ptr& operator=(clone_ptr rhv)
    {
        swap(rhv);
        return *this;
    }
    ~clone_ptr()
    {
        ptr_helper->destroy(ptr);
    }

    T* get() const { /*error checking here*/ return ptr; }
    T& operator* () const { return *get(); }
    T* operator-> () const { return get(); }

    void swap(clone_ptr& other)
    {
        std::swap(ptr, other.ptr);
        ptr_helper.swap(other.ptr_helper);
    }
};

See usage example: http://ideone.com/LnWa3

(But perhaps you don't really need to copy your objects, and might rather explore the possibilities of move semantics. For example, you can have a vector<unique_ptr<T>>, as long as you don't use functions that copy the contents.)

UncleBens
  • 40,819
  • 6
  • 57
  • 90