Boost logo

Boost :

From: Matt Calabrese (rivorus_at_[hidden])
Date: 2006-07-03 16:48:27


On 7/3/06, Thorsten Ottosen <thorsten.ottosen_at_[hidden]> wrote:
>
> As long as C++ does not have move-semantics, the semantics of clone_ptr
> seems to be far too expensive to be useful: std::vector<clone_ptr<T>>
> will have to re-clone all objects on buffer expansion, whereas
> boost::ptr_vector<T> will not.
>

Firstly, while it is true that it may be expensive to copy such objects for
some users, it certainly isn't "far too expensive" for all users, as many
programmers likely are simply not bound by the copy operations of the types
they may be using with it -- an assumption either way made at the library
level, in my opinion, is a mistake in design. Let the user decide what's too
expensive and what isn't, as at the library level you simply don't know
every user's requirements. As well, keep in mind that we are talking about a
trade-off and a change in semantic meaning, not an optimization. If
value semantics are desired for a given type, a programmer shouldn't have to
jump through hoops using a type with
reference semantics and explicit cloning in order to get the functionality
they want -- this is refering to the types in general, not just with respect
to them being stored inside of containers.

But the big point here is that, apart from all of this, even without move
semantics in the language, efficient containment can be accomplished very
easily in the form of compliant STL containers, and I'd argue that it can be
done in a simpler and more intuitive manner than what is provided by the
current pointer containers and even be exactly as efficient! This can be
done by just creating the clone_ptr template and then partially specializing
the standard STL containers rather than working with ptr_container
containers, as is explicitly allowable per 17.4.3.1 paragraph 1 of the
standard. Specializations would be fully compliant to the standard, while
also providing many of the ptr_container optimized operations on top of the
standard operations. The benefit of this is that the user of the library is
really only introduced to one new template, the clone_ptr template, and they
can use that type with any STL container they wish, even with
::std::vector, and the implementation may avoid unecessary copies just like
ptr_vector. The user doesn't even have to be aware of the additional member
functions unless they need such functionality.

This also has several other benefits over the current ptr_containers,
particularly with respect to writing generic code, as instantiating a vector
of clone_ptrs can be done exactly as you would a vector of other types,
making the optimization entirely transparent to the writer of the generic
code. For instance, imagine a type template whose instantiations encapsulate
an ::std::vector of a
type which is specified through an argument of the encapsulating template.
As an example:

template< typename Type >
struct some_type
{
  ::std::vector< Type > container;
};

Now, imagine that
someone instantiating the template wishes to have the internal
container contain
"animals," where animal is an abstract base class and the user would be
storing dynamic instances of cats, dogs, etc. which are derived from the
animal type. This could be desired for some of the same reasons you may wish
to have a container of variants, but with the restriction of used types
being those which are any possible children of a specified type rather than
types which must be individually explicitly specified when instantiating a
variant. With the clone_ptr concept and the associated specializations, the
person would merely instantiate the encapsulating some_type template with
clone_ptr< animal >, and internally the optimized vector implementation
would be used automatically without any explicit intervention on the part of
the person using the template nor the person writing the template.

Without a clone pointer type of concept, it is much more difficult to make
the code both generic and efficient. Specifically, if the above
functionality were desired, the user would likely have to
instantiate some_type with shared_ptrs and then work with reference
semantics in order to get a simple and exception-safe solution, or the
creator of the some_type template would have to make some way for users
instantiating the template to specify the semantics they desire and then
internally use a ptr_vector in cases where it would be acceptable to do so.
Even then, since ptr_containers have subtle differences from their
respective STL containers, additional steps would generally have to be
taken in order to make the code consistent regardless of arguments passed to
some_type. So with clone_ptr, the optimizations stay just as optimizations
in that they do not affect the way generic code is written unless even
further optimizations are required.

Finally, since clone_ptrs are copy constructable and assignable, they can be
used in any containers developed by other programmers right out of the box.
If optimizations are needed, specializations can be made for the containers
rather than entirely new ptr_containers, and all of the benefits from above
once again hold true -- optimizations remain as implementation details
rather than separate containers and any code already written does not have
to be changed in order to take advantage of the optimizations.

-- 
-Matt Calabrese

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk