views:

627

answers:

9

I have class foo that contains a std::auto_ptr member that I would like to copy construct but this does not appear to be allowed. There's a similar thing for the assignment. See the following example:

struct foo
{
private:
    int _a;
    std::string _b;
    std::auto_ptr< bar > _c;

public:
    foo(const foo& rhs)
        :   _a(rhs._a)
        ,   _b(rhs._b)

        ,   _c(rhs._c)
                // error: Cannot mutate rhs._c to give up ownership - D'Oh!
    {
    }

    foo& operator=(const foo& rhs)
    {
         _a = rhs._a;
         _b = rhs._b;

         _c = rhs._c;
             // error: Same problem again.
    }
};

I could just declare _c as mutable but I'm not sure this is correct. Does anyone have a better solution?

EDIT

OK, I'm not getting the kind of answer that I was expecting so I'll be a little more specific about the problem.

  • An object of type foo is created on the stack and passed by value into a container class (not stl) and then goes out of scope. I don't have any control over the container code. (It's actually an active queue implementation, with bugs.)
  • The bar class is a fairly heavyweight parser. It has very poor performance on new and delete so even if it was copy constructable, it would be way too expensive.
  • We can guarantee that when a bar object is created, it will only ever need to be owned in 1 place at a time. In this case it is being passed between threads and deleted when the transaction is completed. This is why I was hoping to use a std::autp_ptr.
  • I am very willing to consider boost smart pointers but I was hoping to guarantee this uniqueness if there is an alternative.
+8  A: 

As you have discovered you can't copy a std::auto_ptr like that. After the copy who owns the object pointed to? Instead you should use a reference counted smart pointer. The Boost library has a shared_ptr you could use.

Martin Liversage
+13  A: 

You might want to try following code:

    foo(const foo& rhs)
        :   _a(rhs._a)
        ,   _b(rhs._b)
        ,   _c(_rhs._c.get() ? new bar(*_rhs._c.get()) : 0)
    {
    }

(Assignment operator is similar.)

However this will only work if bar is CopyConstructible and if this indeed does what you want. The thing is that both foo objects (_rhs and constructed one) will have different pointers in _c.

If you want them to share the pointer then you must not use auto_ptr as it does not support shared ownership. Consider in such case use of shared_ptr from Boost.SmartPtr for example (which will be included in new C++ standard). Or any other shared pointer implementation as this is such a common concept that lots of implementations are available.

Adam Badura
Still the best answer even after the edit. foo will need to either copy or share bar, and auto_ptr<bar> doesn't do sharing well. As copying is now excluded too, you'll need a shared_ptr.
MSalters
A: 

The whole idea of the auto_ptr is that there's only one owner of the referred to object. This implies you cannot copy the pointer without removing the original ownership.

Since you cannot copy it, you also can't copy an object containing an auto_ptr.

You might try to use move-semantics by e.g. using std::swap instead of copy.

xtofl
-1, it's trivial to copy an object containing an auto_ptr<T>. You do so by creating a new auto_ptr<T> pointing to a copied T.
MSalters
@MSalters: but then you aren't copying the auto_ptr. You're copying the referred to object.
xtofl
+3  A: 

First, I'd avoid auto_ptr

Transfer of ownership is good in some scenarios, but I find they are rare, and "full fledged" smart pointer libraries are now available easily. (IIRC auto_ptr was a compromise to include at least one example in the standard library, without the delays that a good implementation would have required).

See, for example here
or here

Decide on semantics
Should the copy of foo hold a reference to the same instance of bar? In that case, use boost::shared_ptr or (boost::intrusive_ptr), or a similar library.

Or should a deep copy be created? (That may sometimes be required, e.g. when having delay-created state). I don't know any standard implementation of that concept, but it's not to complex to build that similar to existing smart pointers.

   // roughly, incomplete, probably broken:
   template <typename T>
   class deep_copy_ptr
   {
      T * p;
     public:
      deep_copy_ptr()  : p(0) {}
      deep_copy_ptr(T * p_)  : p(p_) {}
      deep_copy_ptr(deep_copy_ptr<T> const & rhs)  
      {
        p = rhs.p ? new T(*rhs.p) : 0;
      }
      deep_copy_ptr<T> & operator=(deep_copy_ptr<T> const & rhs)
      {
         if (p != rhs.p)
         {
           deep_copy_ptr<T> copy(rhs);
           swap(copy);
         }
      }
      // ...
   }
peterchen
+1  A: 

The std::auto_ptr is a good tool for managing dynamic object in C++ but in order to use it effectivelly it's important to unserstand how auto_ptr works. This article explains why, when and where this smart pointer should be used.

In your case, first of all your should decide what you want to do with the object inside your auto_ptr. Should it be cloned or shared?

If it should be cloned, make sure it has a copy constructor and then your create a new auto_ptr which contains a copy of your the object see Adam Badura's answer.

If it should shared, you should use boost::shared_ptr as Martin Liversage suggested.

Serge
A: 

My first choice would be to avoid auto_ptr in this situation altogether. But if I were backed against a wall, I might try to use the keyword mutable in the declaration of _c - this will allow it to be modified even from a const reference.

Mark Ransom
A: 

You can't use const references in a copy constructor or assignment operator that involves an auto_ptr<>. Remove the const. In other words, use declarations like

foo(foo & rhs);
foo & operator=(foo & rhs);

These forms are explicitly mentioned in the Standard, primarily in section 12.8. They should be usable in any standard-conforming implementation. In fact, paragraphs 5 and 10 of 12.8 says that the implicitly defined copy constructor and assignment operator (respectively) will take a non-const reference if any of the members require it.

David Thornley
Sure you can use them; you just have to override the implcit copy constructor and assignment operator.
JohnMcG
According to the Standard, they *are* the implicit copy constructor and assignment operator. I gave references. If you know of implementations it won't work on, please tell us.
David Thornley
Your implementation will work, and I think I was incorrect about the implicitly generated part.What I was disagreeing with was the first sentence that you can't use const references in a copy constructor with aut_ptr's. You can, you just have to have them do something different than the default behavior.
JohnMcG
What do you want to do? You can't just copy an auto_ptr<> from a const argument. You can deep copy, which is not desired here. You can create two auto_ptr<>s pointing at the same thing, which is a disaster waiting to happen.
David Thornley
A: 

If I have class containing an auto_ptr, and want deep-copy semantics, I generatally only do this for classes that have a virtual copy operator, i.e. clone().

Then, within the copy constructor, I initialize the auto_ptr to a clone() of the other; e.g.

class Foo
{
   public:
      Foo(const Foo& rhs) : m_ptr(rhs.m_ptr->clone());
   private:
      std::auto_ptr<T> m_ptr;
};

clone() is typically implemented as follows:

class T
{
   std::auto_ptr<T> clone() const
   {
      return std::auto_ptr<T>(new T(*this));
   }
};

We are imposing the condition that T is clonable, but this condition is essentially imposed by having a copiable class with an auto_ptr member.

JohnMcG
A: 

Given the edit, then it appears you want tranfer of ownership semantics.

In that case, then you'll want to have your copy constructor and assignment operator accept non-const references to their arguments, and perform the initialization/assignment there.

JohnMcG