n2351: Improving shared_ptr for C++0x
Jonathan Wakely
jwakely.gcc@gmail.com
Sat Aug 18 20:01:00 GMT 2007
re http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2351.htm
The allocator and aliasing support is now in the working paper so I
have made the changes to GCC's boost_shared_ptr.h
I did this independently of Peter Dimov's changes to Boost, but the
additions are so simple that a quick look at Boost's CVS suggests my
approach is pretty similar. Basically I've added allocator support to
_Sp_counted_base_impl and moved the task of destroying the shared
representation to the subclass where the allocator type is known. It's
the same type-erasure technique used for the deleter. Aliasing is
trivial and just needs new constructors.
My new copyright assignment is in the mail, so I won't submit this
formally until I know that's been received by the FSF, but in the
meantime I have some questions for the list and would be grateful for
feedback.
These changes should only apply to the C++0x shared_ptr, not the TR1
version. I suggest that the new features are always present in
__shared_ptr, _Sp_counted_base etc. but are not exposed in
std::tr1::shared_ptr. Alternatively, taking advantage of 17.4.4.4 lets
you declare additional member functions and add the improvements to
tr1::shared_ptr as well (a conforming program won't use them so
doesn't care if they're present.) Should tr1::shared_ptr support
allocators and aliasing?
Once GCC has std::unique_ptr, should it re-use std::default_delete as
shared_ptr's default deleter, instead of _Sp_deleter? This would be
observable by users, who could use
std::get_deleter<std::default_delete> to test for it. Code that relied
on that implementation detail would not be portable.
The previous question becomes moot if you do away with _Sp_deleter. Is
it worth optimising for the common case of no deleter (which implies
no allocator) by providing a new template:
template <typename _Tp, typename _Lp>
class _Sp_counted_ptr;
as well as the more general
template <typename _Tp, typename _Deleter, typename _Alloc, typename _Lp>
class _Sp_counted_base_impl;
(I don't like the name "base_impl" for a derived class, so have
actually renamed this _Sp_counted_deleter)
Both inherit from _Sp_counted_base but the first one is simpler and
doesn't need to store either a deleter or allocator (the second can
only use the EBO for either the deleter or allocator, not both, so
takes more space on the heap.) Which class is used depends whether a
deleter is passed to the shared_ptr constructor. This means we don't
need _Sp_deleter, and get_deleter<T> will return NULL for all T if the
shared_ptr has no deleter. Should I go with this approach? I've
coded up both forms, so can contribute whichever is preferred.
I see Boost has three classes for this, one with pointer only (p), one
that adds a deleter (pd), and another that adds an allocator (pda).
My version uses the same class for the pd and pda cases, so misses out
a space-optimisation in the case of pd with empty deleter.
I haven't done make_shared and allocate_shared yet, but they're coming
too. IMHO they should be only for C++0x mode, not TR1, or programs
that define the name make_shared and say "using std::tr1;" could fail
to compile.
Thanks for any feedback,
Jon
More information about the Libstdc++
mailing list