
On 7/3/06, Thorsten Ottosen <thorsten.ottosen@dezide.com> 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