Here's a patch to make shared_ptr use allocator_traits, I don't plan to commit this (at least not yet) but it shows what's needed to use allocator_traits instead of the C++03 way of doing things. It uses allocator_traits<T>::__rebind<U>::__type which is a temporary substitute for the unsupported allocator_traits<T>::rebind<U> template alias.
This patch isn't quite backward compatible, because I changed _Sp_counted_deleter::_My_deleter to derive from the supplied _Alloc type instead of the rebound type. I think that's more correct, but changes the type (and possibly size) of the _M_del member. The safe way to make that change is probably to rename _Sp_counted_deleter to _Sp_counted_deleter2, so that code built against the old header uses the old type and code built with the new header uses a new, distinct type. I completely rewrote _Sp_counted_ptr_inplace because it doesn't need a deleter, so doesn't need to derive from _Sp_counted_deleter and can benefit from the EBO. That could be renamed _Sp_counted_ptr_inplace2 for compatibility. Apart from demonstrating how to make use of allocator_traits to support both C++03 and C++11 allocators, this patch allows the following example to work, using allocate_shared to create a shared_ptr to a type with no public constructors or destructor, by using a custom allocator which is declared as a friend: #include <memory> #include <new> template<typename T> struct MyAlloc; class Private { Private() = default; Private(const Private&) = default; ~Private() = default; friend class MyAlloc<Private>; public: int get() const { return 0; } }; template<typename T> struct MyAlloc : std::allocator<Private> { void construct(void* p) { ::new(p) Private(); } }; int main() { MyAlloc<Private> a; auto p = std::allocate_shared<Private>(a); return p->get(); } This allows you to write types which can only be managed
Index: include/bits/shared_ptr_base.h =================================================================== --- include/bits/shared_ptr_base.h (revision 174358) +++ include/bits/shared_ptr_base.h (working copy) @@ -323,18 +323,15 @@ template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp> class _Sp_counted_deleter : public _Sp_counted_base<_Lp> { - typedef typename _Alloc::template - rebind<_Sp_counted_deleter>::other _My_alloc_type; - // Helper class that stores the Deleter and also acts as an allocator. // Used to dispose of the owned pointer and the internal refcount // Requires that copies of _Alloc can free each other's memory. struct _My_Deleter - : public _My_alloc_type // copy constructor must not throw + : public _Alloc // copy constructor must not throw { - _Deleter _M_del; // copy constructor must not throw + _Deleter _M_del; // copy constructor must not throw _My_Deleter(_Deleter __d, const _Alloc& __a) - : _My_alloc_type(__a), _M_del(__d) { } + : _Alloc(__a), _M_del(__d) { } }; public: @@ -353,9 +350,11 @@ virtual void _M_destroy() noexcept { - _My_alloc_type __a(_M_del); - this->~_Sp_counted_deleter(); - __a.deallocate(this, 1); + typedef typename allocator_traits<_Alloc>::template + __rebind_traits<_Sp_counted_deleter>::__type _My_alloc_traits; + typename _My_alloc_traits::allocator_type __a(_M_del); + _My_alloc_traits::destroy(__a, this); + _My_alloc_traits::deallocate(__a, this, 1); } virtual void* @@ -375,51 +374,44 @@ // helpers for make_shared / allocate_shared - template<typename _Tp> - struct _Sp_destroy_inplace - { - void operator()(_Tp* __p) const { if (__p) __p->~_Tp(); } - }; - struct _Sp_make_shared_tag { }; template<typename _Tp, typename _Alloc, _Lock_policy _Lp> - class _Sp_counted_ptr_inplace - : public _Sp_counted_deleter<_Tp*, _Sp_destroy_inplace<_Tp>, _Alloc, _Lp> + class _Sp_counted_ptr_inplace : public _Sp_counted_base<_Lp> { - typedef _Sp_counted_deleter<_Tp*, _Sp_destroy_inplace<_Tp>, _Alloc, _Lp> - _Base_type; + // Helper class that stores the pointer and also acts as an allocator. + // Used to dispose of the owned pointer and the internal refcount + // Requires that copies of _Alloc can free each other's memory. + struct _Impl + : public _Alloc // copy constructor must not throw + { + _Impl(_Alloc __a) : _Alloc(__a), _M_ptr() { } + _Tp* _M_ptr; + }; public: - explicit - _Sp_counted_ptr_inplace(_Alloc __a) - : _Base_type(static_cast<_Tp*>(0), _Sp_destroy_inplace<_Tp>(), __a) - , _M_storage() - { - void* __p = &_M_storage; - ::new (__p) _Tp(); // might throw - _Base_type::_M_ptr = static_cast<_Tp*>(__p); - } - template<typename... _Args> _Sp_counted_ptr_inplace(_Alloc __a, _Args&&... __args) - : _Base_type(static_cast<_Tp*>(0), _Sp_destroy_inplace<_Tp>(), __a) - , _M_storage() + : _M_impl(__a), _M_storage() { - void* __p = &_M_storage; - ::new (__p) _Tp(std::forward<_Args>(__args)...); // might throw - _Base_type::_M_ptr = static_cast<_Tp*>(__p); + _M_impl._M_ptr = static_cast<_Tp*>(static_cast<void*>(&_M_storage)); + allocator_traits<_Alloc>::construct(__a, _M_impl._M_ptr, + std::forward<_Args>(__args)...); // might throw } + virtual void + _M_dispose() noexcept + { allocator_traits<_Alloc>::destroy(_M_impl, _M_impl._M_ptr); } + // Override because the allocator needs to know the dynamic type virtual void _M_destroy() noexcept { - typedef typename _Alloc::template - rebind<_Sp_counted_ptr_inplace>::other _My_alloc_type; - _My_alloc_type __a(_Base_type::_M_del); - this->~_Sp_counted_ptr_inplace(); - __a.deallocate(this, 1); + typedef typename allocator_traits<_Alloc>::template + __rebind_traits<_Sp_counted_ptr_inplace>::__type _My_alloc_traits; + typename _My_alloc_traits::allocator_type __a(_M_impl); + _My_alloc_traits::destroy(__a, this); + _My_alloc_traits::deallocate(__a, this, 1); } // Sneaky trick so __shared_ptr can get the managed pointer @@ -429,13 +421,14 @@ #ifdef __GXX_RTTI return __ti == typeid(_Sp_make_shared_tag) ? static_cast<void*>(&_M_storage) - : _Base_type::_M_get_deleter(__ti); + : 0; #else return 0; #endif } private: + _Impl _M_impl; typename aligned_storage<sizeof(_Tp), alignment_of<_Tp>::value>::type _M_storage; }; @@ -468,18 +461,21 @@ // The allocator's value_type doesn't matter, will rebind it anyway. typedef std::allocator<int> _Alloc; typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type; - typedef std::allocator<_Sp_cd_type> _Alloc2; - _Alloc2 __a2; + typedef typename allocator_traits<_Alloc> ::template + __rebind_traits<_Sp_cd_type>::__type _Alloc_traits; + typename _Alloc_traits::allocator_type __a; + _Sp_cd_type* __mem = 0; __try { - _M_pi = __a2.allocate(1); - ::new(static_cast<void*>(_M_pi)) _Sp_cd_type(__p, __d); + __mem = _Alloc_traits::allocate(__a, 1); + _Alloc_traits::construct(__a, __mem, __p, std::move(__d)); + _M_pi = __mem; } __catch(...) { __d(__p); // Call _Deleter on __p. - if (_M_pi) - __a2.deallocate(static_cast<_Sp_cd_type*>(_M_pi), 1); + if (__mem) + _Alloc_traits::deallocate(__a, __mem, 1); __throw_exception_again; } } @@ -488,18 +484,22 @@ __shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0) { typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type; - typedef typename _Alloc::template rebind<_Sp_cd_type>::other _Alloc2; - _Alloc2 __a2(__a); + typedef typename allocator_traits<_Alloc> ::template + __rebind_traits<_Sp_cd_type>::__type _Alloc_traits; + typename _Alloc_traits::allocator_type __a2(__a); + _Sp_cd_type* __mem = 0; __try { - _M_pi = __a2.allocate(1); - ::new(static_cast<void*>(_M_pi)) _Sp_cd_type(__p, __d, __a); + __mem = _Alloc_traits::allocate(__a2, 1); + _Alloc_traits::construct(__a2, __mem, + __p, std::move(__d), std::move(__a)); + _M_pi = __mem; } __catch(...) { __d(__p); // Call _Deleter on __p. - if (_M_pi) - __a2.deallocate(static_cast<_Sp_cd_type*>(_M_pi), 1); + if (__mem) + _Alloc_traits::deallocate(__a2, __mem, 1); __throw_exception_again; } } @@ -510,18 +510,19 @@ : _M_pi(0) { typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type; - typedef typename _Alloc::template rebind<_Sp_cp_type>::other _Alloc2; - _Alloc2 __a2(__a); + typedef typename allocator_traits<_Alloc> ::template + __rebind_traits<_Sp_cp_type>::__type _Alloc_traits; + typename _Alloc_traits::allocator_type __a2(__a); + _Sp_cp_type* __mem = _Alloc_traits::allocate(__a2, 1); __try { - _M_pi = __a2.allocate(1); - ::new(static_cast<void*>(_M_pi)) _Sp_cp_type(__a, + _Alloc_traits::construct(__a2, __mem, std::move(__a), std::forward<_Args>(__args)...); + _M_pi = __mem; } __catch(...) { - if (_M_pi) - __a2.deallocate(static_cast<_Sp_cp_type*>(_M_pi), 1); + _Alloc_traits::deallocate(__a2, __mem, 1); __throw_exception_again; } }