Conversions between shared_ptr<Base> and shared_ptr<Derived>

I've been using shared pointers for a few months now and just discovered I have been using them the wrong way, causing massive memory leaks. So I'm going back through my code and cleaning everything up and making the changes to correct all the mistakes I had been making. So now I've run into a bit of a problem. I'm using Boost 1.30.2 currently and I have function that takes in a Base class to place into a vector, but I have a number of Derived classes that will be going into the vector as well. From reading through the docs and the mailing lists everything seems to indicate that the conversion should be implicit and there shouldn't be a problem. I have the feeling I am missing something here so I was wondering if someone could point out my error. So I have something like this: std::vector<boost::shared_ptr<Base> > BaseVector; void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); } and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p); I thought that it would work just like using raw pointers (which was working) but I get a compile time error that : no matching function call to add(boost::shared_ptr<Derived>&) canadites are add(boost::shared_ptr<Base>&).. ect. So I think I'm doing this the wrong way, about the only thing I can think of was something I saw in a few postings. Essentially creating a new pointer with the derived pointer then passing it into the function like this : boost::shared_ptr<Derived> p(new Derived); boost::shared_ptr<Base> q(p); add(q); Is that the preferred way of doing it? Or is there another step I'm missing when creating the shared_ptrs to allow them to be implicitly converted? Thanks, Joshua.

So I have something like this:
std::vector<boost::shared_ptr<Base> > BaseVector;
void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); }
and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p);
I think the problem is you take a reference to the shared_ptr. Try this way: void add(boost::shared_ptr<Base> newOne) { BaseVector.push_back(newOne); } -- Michele Galante Zucchetti Centro Sistemi SPA m.galante@centrosistemi.it

Michele Galante wrote:
So I have something like this:
std::vector<boost::shared_ptr<Base> > BaseVector;
void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); }
and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p);
I think the problem is you take a reference to the shared_ptr. Try this way:
void add(boost::shared_ptr<Base> newOne) { BaseVector.push_back(newOne); }
Ah, that seemed to have fixed the problem. Thank you, Joshua.

For performance reasons I would like to see something like the following conversion operators in shared_ptr<T>: template<class T> class shared_ptr { public: ... template<class S> operator shared_ptr<S>&() throw() { BOOST_STATIC_ASSERT(T* is convertible to S*); return reinterpret_cast<shared_ptr<S>&>(*this); } template<class S> operator const shared_ptr<S>&() throw() const { BOOST_STATIC_ASSERT(T* is convertible to S*); return reinterpret_cast<const shared_ptr<S>&>(*this); } }; // class shared_ptr<T> This would mimic the standard conversion from T* to S* without any performance penalty for shared_ptr<T>. Konstantin "Michele Galante" <m.galante@centrosistemi.it> schrieb im Newsbeitrag news:4020B616.7040900@centrosistemi.it...
So I have something like this:
std::vector<boost::shared_ptr<Base> > BaseVector;
void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); }
and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p);
I think the problem is you take a reference to the shared_ptr. Try this way:
void add(boost::shared_ptr<Base> newOne) { BaseVector.push_back(newOne); }
-- Michele Galante Zucchetti Centro Sistemi SPA m.galante@centrosistemi.it

Konstantin Baumann wrote:
For performance reasons I would like to see something like the following conversion operators in shared_ptr<T>:
template<class T> class shared_ptr { public: ... template<class S> operator shared_ptr<S>&() throw() { BOOST_STATIC_ASSERT(T* is convertible to S*); return reinterpret_cast<shared_ptr<S>&>(*this); }
[...] Right. ;-) There are several things wrong with this code. First, T* convertible to S* does not imply that the memory layout of T* and S* is the same. Specifically, T* and void* may have different representations and sizes. reinterpret_casting a T* to a void*& is not portable. Second, assuming that memory layout is not an issue, there is the classic "nuclear submarine in a car park" type system hole. void f(shared_ptr<void> & p) { p.reset(new float); } int main() { shared_ptr<int> q(new int); // see int, think 'car' f(q); // oops, q now holds a float (nuke sub) } Third, the conversion between T* and S* may change the value of the pointer. In multiple inheritance scenarios (and, depending on the object model, even in some single inheritance cases) an offset is added to T*. With virtual inheritance, it's even more complicated; typically the vtable of the source object of type T is accessed to retrieve the offset to the S virtual base. In short, even though it is no coincidence at all that a shared_ptr<X> is often layout compatible and (somewhat safely) reinterpret_cast'able to shared_ptr<Y>, you need to accept the responsibility for the cast yourself.

Joshua Little wrote: [...]
So I have something like this:
std::vector<boost::shared_ptr<Base> > BaseVector;
void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); }
and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p);
I thought that it would work just like using raw pointers (which was working) but I get a compile time error that : no matching function call to add(boost::shared_ptr<Derived>&) canadites are add(boost::shared_ptr<Base>&).. ect.
It should fail with raw pointers, too, unless your compiler is too permissive. The problem is that void add(boost::shared_ptr<Base>& newOne) takes a non-const reference to shared_ptr<Base>, so you need a shared_ptr<Base> to call it. Change it to void add(boost::shared_ptr<Base> const & newOne) and it should work. And, as a general rule, always use a reference to const when you don't intend to modify the argument.

--- At Wed, 4 Feb 2004 15:29:50 +0200, Peter Dimov wrote:
Joshua Little wrote: [...]
So I have something like this:
std::vector<boost::shared_ptr<Base> > BaseVector;
void add(boost::shared_ptr<Base>& newOne) { BaseVector.push_back(newOne); }
and I call it like this usually : boost::shared_ptr<Derived> p(new Derived); add(p);
I thought that it would work just like using raw pointers (which was working) but I get a compile time error that : no matching function call to add(boost::shared_ptr<Derived>&) canadites are add(boost::shared_ptr<Base>&).. ect.
It should fail with raw pointers, too, unless your compiler is too permissive. The problem is that
void add(boost::shared_ptr<Base>& newOne)
takes a non-const reference to shared_ptr<Base>, so you need a shared_ptr<Base> to call it. Change it to
void add(boost::shared_ptr<Base> const & newOne)
and it should work. And, as a general rule, always use a reference to const when you don't intend to modify the argument.
Peter is quite correct with the change to the argument type. It seems to me the solution could be had another way: boost::shared_ptr<Base> p(new Derived); ---- add(p); Then p is the correct type. The value being added to the vector will be a Base and not a Derived, so you loose nothing by using the shared_ptr as a Base. The only catch would be using Derived "features" between the construction of the shared pointer and the addition to the vector. ...Duane
participants (5)
-
Duane Murphy
-
Joshua Little
-
Konstantin Baumann
-
Michele Galante
-
Peter Dimov