How to use allocators in modern C++

3.9k views Asked by At

From what I read in http://en.cppreference.com/w/cpp/memory/allocator , most features of the allocators are now going to be deprecated. The question is, how is one supposed to use allocators in new code? What is the "right" way now?

From what I deduce in the documentation, construct is part of the allocator traits, rather than the allocator itself.

I am building a custom container, here it is a very simple version of the constructor, is this a good usage of the new design?

container::container(std::size_t size, T const& value, Allocator const& allocator) : allocator_(allocator){
    data_ = std::allocator_traits<Alloc>::allocate(allocator_, size);
    for(auto ptr = data_; ptr != data_ + size; ++ptr){
        std::allocator_traits<Allocator>::construct(allocator_, ptr, value)
    }
}

I tried to use an algorithm (like std::for_each) in the loop but I didn't manage to use one without taking addresses (operator&).

Where can I find a complete example of a modern allocator?


After some tweaking, I found a way to use an algorithm instead of the the raw loop (to which an execution policy can be passed). I am not very sure, but it could be this:

    data_ = std::allocator_traits<Allocator>::allocate(allocator_, size);
    std::for_each([policy? deduced from allocator?,] 
        boost::make_counting_iterator(data_),
        boost::make_counting_iterator(data_ + size), 
        [&](auto ptr){std::allocator_traits<Allocator>::construct(allocator_, ptr, value);}
    );
1

There are 1 answers

4
milleniumbug On BEST ANSWER

Yes, the current approach is through std::allocator_traits. You'll be able to support the "minimal allocator interface" that way.

http://en.cppreference.com/w/cpp/concept/Allocator

Some requirements are optional: the template std::allocator_traits supplies the default implementations for all optional requirements, and all standard library containers and other allocator-aware classes access the allocator through std::allocator_traits, not directly.

If you observe the std::allocator_traits member functions and typedefs, you'll see they're detecting the presence of appropriate function/types and dispatching through them if they can.

The deprecation and potential future removal will change nothing if you're already using std::allocator_traits since it only applies to std::allocator and their member functions/typedefs.

Now, if you ask me, there's nothing wrong with for-loops, and using std::for_each gains you nothing. There are several uninitialized_* functions, but they use placement new directly. If you really care you can extract this code to a separate construct_range function.

There's also an exception safety issue - in case one of the constructors throws, you need to destroy the earlier elements in order to satisfy the strong exception guarantee and free the memory too (destructor won't get called in case constructor throws)