Thoughts on make_shared and shared_from_this in a constructor

Let's take an example make_shared overload: template< class T, class A1, class A2 > boost::shared_ptr< T > make_shared( A1 const & a1, A2 const & a2 ) { boost::shared_ptr< T > pt( static_cast< T* >( 0 ), detail::sp_ms_deleter< T >() ); detail::sp_ms_deleter< T > * pd = boost::get_deleter< detail::sp_ms_deleter< T > >( pt ); void * pv = pd->address(); // #1 ::new( pv ) T( a1, a2 ); pd->set_initialized(); T * pt2 = static_cast< T* >( pv ); boost::detail::sp_enable_shared_from_this( &pt, pt2, pt2 ); return boost::shared_ptr< T >( pt, pt2 ); } The primary problem with calling shared_from_this in a constructor is that a shared_ptr to the object does not exist yet and may never will. But, when using make_shared, at the line marked //#1 we do have a shared_ptr that owns the storage of the future object T. So we should, in principle, be able to return a copy of it from shared_from_this. Unfortunately, we can't just move the sp_enable_shared_from_this call to #1 because (a) we can't convert an yet-unconstructed T to its enable_shared_from_this base if there's virtual inheritance along the way, and (b) even if we could, we can't initialize the weak_this_ member since it's not constructed yet. I can't think of a good way to pass this shared_ptr to the enable_shared_from_this base since we have no control over T.

On Saturday 07 March 2009, Peter Dimov wrote:
I can't think of a good way to pass this shared_ptr to the enable_shared_from_this base since we have no control over T.
But we do have control over T. That is, if the user wants to define a class T which uses shared_from_this in its constructor, we can require them to fulfill certain requirements, right? Maybe something like providing a special constructor signature which accepts the owning shared_ptr, plus maybe an extra tag object to insure it doesn't get confused with other constructor overloads. Although if the T constructor is receiving the shared_ptr directly, it wouldn't have any need to call shared_from_this().

Frank Mori Hess:
On Saturday 07 March 2009, Peter Dimov wrote:
I can't think of a good way to pass this shared_ptr to the enable_shared_from_this base since we have no control over T.
But we do have control over T. That is, if the user wants to define a class T which uses shared_from_this in its constructor, we can require them to fulfill certain requirements, right? Maybe something like providing a special constructor signature which accepts the owning shared_ptr, plus maybe an extra tag object to insure it doesn't get confused with other constructor overloads. Although if the T constructor is receiving the shared_ptr directly, it wouldn't have any need to call shared_from_this().
This is one possible solution. Define a "token class" weak_this into which make_shared can stuff the owning shared_ptr, so that when make_shared<X>( weak_this(), 1, 2 ) is called, X::X receives a non-empty weak_this. This works but requires cooperation from X. In principle, X may be derived from Y which in turn is derived from enable_shared_from_this<Y>. Y knows about weak_this but X may not. Another possible solution is to stick the "weak this" into a thread-local static member of enable_shared_from_this<Y>. This also works but (absent compiler support for thread locals) doesn't fit into the header-only nature of shared_ptr.

On Saturday 07 March 2009, Peter Dimov wrote:
Another possible solution is to stick the "weak this" into a thread-local static member of enable_shared_from_this<Y>. This also works but (absent compiler support for thread locals) doesn't fit into the header-only nature of shared_ptr.
It could be added as a extra feature that only exists for compilers with support for thread local variables (or when threading is disabled). And since thread_local is in c++0x, it's no barrier for the feature being added at some point to std::enable_shared_from_this.
participants (2)
-
Frank Mori Hess
-
Peter Dimov