Reputation: 222
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.
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.)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.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
.Upvotes: 8
Views: 7943
Reputation: 8825
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.
Upvotes: 0
Reputation: 8825
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.
Upvotes: 1
Reputation: 57076
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.
Upvotes: -1
Reputation: 308548
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.
Upvotes: 0
Reputation: 41519
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.
Upvotes: 0
Reputation: 7694
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.
Upvotes: 1
Reputation: 41146
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).
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);
}
}
// ...
}
Upvotes: 3
Reputation: 5349
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.
Upvotes: 16
Reputation: 106956
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.
Upvotes: 8