Reputation: 10872
I implemented my custom allocator and can use it like so with STL containers:
std::map<int, int, std::less<int>, CustomAllocator<int,100>> m1;
Now I want to create a custom container which supports this custom allocator. I know how to do it in C++17, using pmr::polymorphic_allocator<byte>
. So, lets say we have some Node
structure and a custom slist
container class, which stores these nodes. So, to use our custom allocator, we would create a member in our class:
allocator_type m_allocator;
where allocator_type is defined like so:
using allocator_type = pmr::polymorphic_allocator<byte>;
and in slist
methods where we need our allocator, we can use it something like:
//in insert method, construct new Node to store
m_allocator.construct(...);
And our client code would look like:
test_resource tr; // our custom allocator
slist<pmr::string> lst(&tr);
But how can I achieve the same thing in C++11/14? What should I specify in my custom container to use my CustomAllocator
?
Upvotes: 4
Views: 753
Reputation: 171177
Probably the simplest solution is to follow the model of the standard library and give your container a template parameter for the allocator it is to use.
When you do that, do not forget that the standard library (from C++11 onwards) requires all access to allocators to go through std::allocator_traits
instead of accessing the allocator object's members directly (since it may not have them all). You should do likewise, to be compatible with other allocators designed for use with the standard library.
As an example of using allocator traits, consider this contrived "container":
template <class A>
struct StringContainer
{
std::string *data;
A allocator;
StringContainer(std::string value, A allocator);
~StringContainer();
};
The following would be the wrong way of implementing the constructor:
StringContainer(std::string value, A a) : allocator(a)
{
data = allocator.allocate(sizeof(int));
allocator.construct(data, value);
}
The reason is that allocators are not required to provide the construct
member. If they don't provide one, placement new
is to be used. The correct way to implement the constructor is therefore:
StringContainer(std::string value, A a) : allocator(a)
{
data = std::allocator_traits<A>::allocate(allocator, 1);
std::allocator_traits<A>::construct(allocator, data, value);
}
It is std::allocator_traits<A>::construct
which takes care of calling construct
if A
supports it, or placement new
if it does not.
Likewise, the destructor should be implemented like this:
~StringContainer()
{
std::allocator_traits<A>::destroy(allocator, data);
std::allocator_traits<A>::deallocate(allocator, data, 1);
}
Actually, even the class is somewhat wrongly implemented. data
's type should be:
typename std::allocator_traits<A>::pointer data;
Upvotes: 3