Question

I don't like that I have to repeat the contained typename if I use a custom allocator for a container:

template<typename T, size_t MyAllocatorArgument>
struct MyAllocator : public std::allocator<T>
{
    // ... Usual allocator implementation
};

typedef std::vector<int, MyAllocator<int, 42>> int_container;
typedef std::vector<int, MyAllocator<long, 12>> int_container_wrong_allocator;

The second line is undefined behaviour according to the standard, though most implementations will rebind the allocator to the correct type.

My question is, given that it's a requirement for the container and allocator to be for the same type, why is there not some standard machinery in place to enforce this (or avoid it entirely) and remove the potential for user error?

For example, the standard could mandate that rebind does get used (to effectively make the allocator template parameter redundant), or a pattern like the below could be used so the user only mentions the contained typename once:

template<size_t MyAllocatorArgument>
struct MyAllocator
{
    // This would be something every allocator is required to expose.
    template<typename T>
    struct TypedAllocator : public std::allocator<T>
    {
        // This is where the normal implementation of the allocator would go.
        // allocate, deallocate etc.
    };
};

template<typename T, typename UntypedAllocator>
struct Container
{
    // All containers would do this to get the actual allocator type they would use.
    typedef typename UntypedAllocator::template TypedAllocator<T> TypedAllocator;

    Container() : m_allocator(TypedAllocator()) {}

    void useAllocator()
    {
        m_allocator.allocate();
        // ... or whatever else containers need to do with allocators.
    }

    TypedAllocator m_allocator;
};

void allocator_test()
{
    // Allocated type name isn't mentioned at point of use of container;
    // only once for the container. The container does all the work.
    Container<int, MyAllocator<42>> c1;
}
Was it helpful?

Solution

It's a good question, and your suggestion is one possible alternative to the standard scheme. Another would have been to use template template parameters:

template<typename T>
class AnAllocator
{ ... };

template<typename T, template <typename> class Alloc = std::allocator>
class Vector
{
  typedef Alloc<T> allocator_type;
  ...
};

Vector<int, AnAllocator> v;

The allocator interface was designed before template template parameters were part of the language, so that wasn't an option.

There are lots of things that would be done differently if the allocator API were to be designed today, unfortunately we're stuck with the one we have (and the continuing complexity caused by trying to extend it in semi-compatible ways).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top