
Christian Schladetsch skrev:
On Fri, Jun 26, 2009 at 9:26 AM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
Christian Schladetsch skrev:
How does a clone_allocator make a new clone, if it needs access to the allocator in the parent ptr_container? [snip]
What am I missing?
Nothing, I think. This seems like something it was not designed for, on the cnotrary the clone is currently always allocated by someone else.
Then what is the point of clone_allocator?
It does provide some room for customization. But it was deliberately kept very conservative, so we can extend it when we need to in light of user experience.
The current design uses static functions, and so there can be no stateful allocator.
I thought a little about this, but have not come up with a solution. For example, how do we design an allocator that knows how to clone a class hierarchy? Only the class itself knows how to clone itself because it knows the exact type. OTOH, the classes in the class hieararchy doesn't know about the allocator.
We might try a new way to clone objects by replacing
virtual Base::clone() const = 0;
Isn't this what a copy constructor is for?
Types in object hierarchies usually don't expose their copy-constructor to avoid sliving. Furthermore, and more importantly, you loose the concrete type of the objects by having a container of pointers to the base class. Virtual functions are the most elegant way to ask the object for a copy of the right type.
I've had a quick look at <boost/ptr_container/ptr_inserter.hpp> and related files, and it seems that whenever it currently says something like
obj = container_type::clone_allocator_type::allocate_clone(*r);
it could say instead
obj = container_type::clone_allocator_type::allocate_clone(*r, cont.get_allocator());
I guess it could.
And then in a custom clone_allocator, you could say:
struct my_clone_allocator { template< class U, class Allocator > static U* allocate_clone( const U& r, Allocator &alloc) { typename Allocator::template rebind<U>::other my_alloc(alloc); U *clone = my_alloc.allocate(1); my_alloc.construct(clone, r); return clone; } }
This requires a public copy-constructor which we must not impose as a requirement, and even so it would not work because the concrete type is lost. (let's ignore that it is not exception-safe). But this raises another question I have: would we want all the objects to be allocated from the same allocator type using rebind<>, or would we like to use two different allocators.
Or, pass in a rebound allocator to allocate_clone; it doesn't really matter. Point being, can you just pass the container's allocator to the clone_allocator?
I can, but I feel it is unanswered whether it should be the underlying container's allocator or simply the clone_allocator that somehow makes it's own allocations. And, as stated above, we also need to make sure that implementers of clone() get the allocator. I'm not a fan of having users call rebind<> inside each of their virtual clone() functions. It sounds very inelegant to me. That is why I suggested a non-templated allocator, boost::clone_allocator, that can be used by all classes in the hierarchy.
Another alternative is to make clone_allocator a concept and pass the either-or-both U and Allocator types to it at compile time, but I think it works just as well and more generally as a static method.
We have to allow non-static functions to support Robert's use case. -Thorsten