I would like to design a class template which takes an allocator type (as defined in Standard section 17.6.3.5) as a template argument. I see how std::allocator_traits<A> helpfully fills in any missing members of A with default settings. Beyond that, is there anything in the Standard Library or boost that would help use the allocator correctly?
In particular:
-
To honor the typedefs like
std::allocator_traits<A>::propagate_on_container_copy_assignment, do I have to check these things in the special member functions of each class which has a member of typeA? Or is there some wrapper type I could use as a member instead which would take care of this stuff? -
If I want to overallocate to reduce the number of allocations by storing extra data next to the user-visible objects, is it appropriate to rebind the allocator something like this?
.
template<typename T, typename A>
class MyClass
{
private:
//...
struct storage {
int m_special_data;
T m_obj;
};
typedef typename std::allocator_traits<A>::template rebind_alloc<storage>
storage_alloc;
typedef typename std::allocator_traits<A>::template rebind_traits<storage>
storage_traits;
storage_alloc m_alloc;
static T* alloc(T&& obj)
{
storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1);
sp->m_special_data = 69105;
return ::new(&sp->m_obj) T(std::move(obj));
}
//...
};
I don’t know of anything to make life easier,
allocator_traitsreally makes it simpler to write an allocator, by providing all the boilerplate code, but it doesn’t help use an allocator.So that I could use a single allocator API in both C++03 and C++11 code I added
<ext/alloc_traits.h>to GCC 4.7, the class template__gnu_cxx::__alloc_traitsprovides a consistent API that usesallocator_traitsin C++11 mode and calls the relevant member functions directly on the allocator in C++03 mode.No, there’s no wrapper or shortcut, the C++11 allocator requirements make a container author’s job much more complicated. The requirements are slightly different for each container, depending on how it manages memory. For a vector-like type, in the copy-assignment operator if
propagate_on_container_copy_assignment(POCCA) is false and the existing capacity is greater than the source object’s size then you can re-use the existing memory (if POCCA is true and the new allocator is not equal you can’t re-use the old memory as it won’t be possible to de-allocate it later after the allocator is replaced) but that optimization doesn’t help much for a node-based container such as list or map.That looks almost right, although you probably want to replace
with
As stated in [container.requirements.general]/3 the containers which use an allocator use
allocator_traits<A>::constructto create the element typeTitself but any other types allocated (such as yourstorage) must not useconstruct.If
storageitself is constructed then it will constructstorage::m_objunless that member is a type which can be left uninitialized, such asstd::aligned_storage<sizeof(T)>, that can be initialized explicitly later byallocator_traits<A>::construct. Alternatively, individually construct each member that needs non-trivial construction e.g. ifstoragealso had astringmember:The
m_special_datamember is a trivial type so its lifetime begins as soon as storage is allocated for it. Them_strandm_objmembers need non-trivial initialization so their lifetimes begin when their constructors complete, which is done by the placement new and theconstructcall, respectively.Edit: I’ve recently learnt that the standard has a defect (which I’ve reported) and the calls to
constructdo not need to use a rebound allocator, so these lines:can be replaced with:
Which makes life slightly easier.