Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of the static member functions in STL's allocator_traits?

I am trying to implement an STL-style container class, and I have a question with respect to the use of allocators within my class:

What is the purpose of the static member functions in STL's allocator_traits?

Until now, I thought that I am supposed to instantiate the allocator_type (probably via some kind of empty base optimization to improve the memory footprint). Thus, I would end up with something like this:

struct EmptyBaseOpt : allocator_type
{
  EmptyBaseOpt(const allocator_type & a, allocator_type::const_pointer p)
  : allocator_type(a), prefix_ptr(p) { }

  allocator_type::pointer prefix_ptr;
}

EmptyBaseOpt ebo;

And then, I could use the allocator in the following way:

allocator_type & alloc = ebo;
alloc.allocate(100, ebo.prefix_ptr);

On the other hand, the allocator_traits in C++11 seem to imply the following usage:

std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);

I guess this static allocate member function will probably create a temporary ad-hoc instance of allocator_type via its default constructor. But this leads to the following questions:

  1. What will happen if allocator_type is a stateful allocator? Will such allocators be able to keep their state if I use the static member functions in allocator_traits instead of calling the non-static methods from an instance of allocator_type?

  2. Why should I instantiate allocator_type at all and bother with stuff like EBO if I can directly use the static member functions in allocator_traits instead?

  3. As stated before, my understanding is that any class-like template parameters should be instantiated inside my container class in order to allow for stateful versions of these parameters. Is this understanding correct, and how does it fit with the static member functions in allocator_traits?

like image 703
MAA1612 Avatar asked Dec 19 '22 00:12

MAA1612


1 Answers

On the other hand, the allocator_traits in C++11 seem to imply the following usage:

std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);

No, you're missing the most important argument to that function call: the allocator.

I guess this static allocate member function will probably create a temporary ad-hoc instance of allocator_type via its default constructor.

No, because you pass an allocator argument to the function.

What will happen if allocator_type is a stateful allocator?

It works fine, because you pass that stateful allocator as an argument to the functions that use it.

Why should I instantiate allocator_type at all and bother with stuff like EBO if I can directly use the static member functions in allocator_traits instead?

Because you can't use them instead.

As stated before, my understanding is that any class-like template parameters should be instantiated inside my container class in order to allow for stateful versions of these parameters. Is this understanding correct, and how does it fit with the static member functions in allocator_traits?

Yes, your understanding is correct, and it fits fine with allocator_traits if you use it properly.

The point of allocator_traits is to provide sensible defaults for most of the Allocator interface. This has two purposes:

  • firstly it is simpler to define an allocator in C++11 (you only need to provide value_type, allocate, deallocate, a template constructor for re-binding the allocator and operator== and operator!=) so writing simple custom allocators now is much simpler.

  • secondly it allows existing allocators that only meet the C++03 allocator requirements to be used by C++11 containers. C++03 allocators do not define the nested members such as propagate_on_container_swap that C++11 containers look for, or the new variadic construct(pointer, Args&&...) member that allows objects to be constructed with any arguments, not just copy constructed (which is what allows emplace to work). So by wrapping use of allocators in allocator_traits most of the Allocator interface is given sensible defaults, so that existing C++03 code using a custom allocator with a container such as std::vector won't suddenly fail to build if recompiled using C++11. The std::vector implementation only uses the new members through the allocator_traits type and so it doesn't matter that the custom allocator wasn't updated to provide all the new members that are part of the C++11 Allocator requirements.

like image 107
Jonathan Wakely Avatar answered Dec 21 '22 15:12

Jonathan Wakely