Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
229 views
in Technique[技术] by (71.8m points)

c++ - What is the purpose of std::scoped_allocator_adaptor?

In the C++11 standard we have std::scoped_allocator_adaptor in the dynamic memory management library. What are the most important use cases of this class?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If you want a container of strings and want to use the same allocator for the container and its elements (so they are all allocated in the same arena, as TemplateRex describes) then you can do that manually:

template<typename T>
  using Allocator = SomeFancyAllocator<T>;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using Vector = std::vector<String, Allocator<String>>;

Allocator<String> as( some_memory_resource );
Allocator<char> ac(as);
Vector v(as);
v.push_back( String("hello", ac) );
v.push_back( String("world", ac) );

However, this is awkward and error-prone, because it's too easy to accidentally insert a string which doesn't use the same allocator:

v.push_back( String("oops, not using same memory resource") );

The purpose of std::scoped_allocator_adaptor is to automatically propagate an allocator to the objects it constructs if they support construction with an allocator. So the code above would become:

template<typename T>
  using Allocator = SomeFancyAllocator<T>;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using Vector = std::vector<String, std::scoped_allocator_adaptor<Allocator<String>>>;
                                   /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
Allocator<String> as( some_memory_resource );
Allocator<char> ac(as);
Vector v(as);
v.push_back( String("hello") );  // no allocator argument needed!
v.push_back( String("world") );  // no allocator argument needed!

Now the vector's allocator is automatically used to construct its elements, even though the objects being inserted, String("hello") and String("world"), are not constructed with the same allocator. Since basic_string can be implicitly constructed from const char* the last two lines can be simplified even further:

v.push_back( "hello" );
v.push_back( "world" );

This is much simpler, easier to read, and less error-prone, thanks to scoped_allocator_adaptor constructing the elements with the vector's allocator automatically..

When the vector asks its allocator to construct an element as a copy of obj it calls:

std::allocator_traits<allocator_type>::construct( get_allocator(), void_ptr, obj );

Normally the allocator's construct() member would then call something like:

::new (void_ptr) value_type(obj);

But if the allocator_type is scoped_allocator_adaptor<A> then it uses template metaprogramming to detect whether value_type can be constructed with an allocator of the adapted type. If value_type doesn't use allocators in its constructors then the adaptor does:

std::allocator_traits<outer_allocator_type>::construct(outer_allocator(), void_ptr, obj);

And that will call the nested allocator's construct() member, which uses something like placement new, as above. But if the object does support taking an allocator in its constructor then the scoped_allocator_adaptor<A>::construct() does either:

std::allocator_traits<outer_allocator_type>::construct(outer_allocator(), void_ptr, obj, inner_allocator());

or:

std::allocator_traits<outer_allocator_type>::construct(outer_allocator(), void_ptr, std::allocator_arg, inner_allocator(), obj);

i.e. the adaptor passes additional arguments when it calls construct() on its nested allocator, so that the object will be constructed with the allocator. The inner_allocator_type is another specialization of scoped_allocator_adaptor, so if the element type is also a container, it uses the same protocol to construct its elements, and the allocator can get passed down to every element, even when you have containers of containers of containers etc.

So the purpose of the adaptor is to wrap an existing allocator and perform all the metaprogramming and manipulation of constructor arguments to propagate allocators from a container to its children.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...