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);
 }
asked Nov 29, 2011 at 18:48

3 Answers 3

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)
{
}
answered Nov 29, 2011 at 18:54
Sign up to request clarification or add additional context in comments.

5 Comments

@ 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: 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).
@Tin The C++FAQ calls that "virtual constructor"
@MikeSeymour @Cubbi if I've now a class with a private member: std::vector<std::unique_ptr<Base> > 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<T>::MyCollecyion(const refCol):myvector(refCol.myvector){}, I get a compiler error.
@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?
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.

answered Nov 29, 2011 at 18:56

2 Comments

@ 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?
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.
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.)

answered Nov 29, 2011 at 21:47

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.