Re: boost::shared_ptr used in covariant return type
zade wrote:
boost::shared_ptr is good for return type when you create a new type
using pimpl idiom, but in practice, it gives me some trouble when used
for covariant return type. code like below:
struct Object
{[...]};
typedef boost::shared_ptr<Object> ObjectPtr;
struct Cloneable: public Object
{ [...]
virtual ObjectPtr Clone() const = 0;// it is wrong in this form, see
Foo below
};
Note: I'm not sure why you separated the Clonable type from the Object type.
struct Foo;
typedef boost::shared_ptr<Foo> FooPtr;
struct Foo : public Cloneable
{
//virtual Foo* Clone() const // it is ok in this form
//{
// return new Foo();
//}
virtual FooPtr Clone() const// it is wrong in this form
{
return boost::shared_ptr<Foo>(new Foo());
}
};
Well, some trouble would be easy, but the problem is simply that shared_ptrs
are not derived from one another just because their pointees are. So, what
could you do...
1. You could create a type like shared_ptr (BTW: I would use std::auto_ptr
in this context, but that's beside the point) where magic_handle<Foo>
derives from magic_handle<Clonable>. This can actually be done with some
work for each derived type but it doesn't do itself.
2. You could overload clone() for every derived class.
Note that you should restructure this anyway:
struct clonable {
auto_ptr<clonable> clone() const {
auto_ptr<clonable> tmp(do_clone());
assert(tmp.get()); // must be non-null
assert(typeid(*tmp)==typeid(*this)); // must be same type
return tmp;
}
protected:
virtual clonable* do_clone() const = 0;
};
This performs two checks: firstly that the clone and the original are really
the same type and not only share one common baseclass. Secondly, this
assures that the returned pointer is non-null.
Now, we can write the derived class like this while following approach #2
above:
struct derived: clonable {
auto_ptr<derived> clone() const {
auto_ptr<derived> tmp(do_clone());
assert(tmp.get()); // must be non-null
assert(typeid(*tmp)==typeid(*this)); // must be same type
return tmp;
}
protected:
virtual derived* do_clone() const {
return new derived(*this);
}
};
This then firstly repeats the code from the baseclass for the nonvirtual
clone() except for the type and implements do_clone() in a way that allows
a covariant returntype. This do_clone() then both fits clonable::clone()
and derived::clone().
I haven't found too much use for this, because if you know you have
a 'derived', you can also call 'new derived(obj)' instead, barring types
further derived though. However, I think you should be able to factor the
repeated code for clone() and do_clone() into a mixin using the CRTP:
template<typename clonable_type>
struct clonable_impl {
auto_ptr<clonable_type> clone() const {
...
}
protected:
virtual clonable_type* do_clone() const {
return new clonable_type(static_cast<clonable_type const&>(*this));
}
};
struct derived: clonable_impl<derived>, clonable {
...
};
I didn't test this (never needed such code) and I think that the do_clone()
function can't be implemented by a mixin because the mixin does not take
part when resolving virtual functions for other baseclasses. Maybe if the
implementation type took two template parameters, the derived one and the
base one and then derived from the base itself it would work. I'm not sure
if the hassle is worth the effort.
I'll leave the rest as exercise for the reader. ;)
Uli
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]