本文介绍了std :: scoped_allocator_adaptor的用途是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ 11标准中,动态内存管理库中有std::scoped_allocator_adaptor.此类中最重要的用例是什么?

解决方案

如果您想要一个字符串容器,并且想要为该容器及其元素使用相同的分配器(因此它们都与TemplateRex一起分配在同一区域中)说明),那么您可以手动执行此操作:

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) );

但是,这很尴尬且容易出错,因为很容易意外插入不使用同一分配器的字符串:

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

std::scoped_allocator_adaptor的目的是自动将分配器传播到它构造的对象(如果它们支持使用分配器构造的对象).因此,上面的代码将变为:

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!

现在,即使要插入的对象String("hello")String("world")不是使用相同的分配器构造的,向量的分配器也会自动用于构造其元素.由于basic_string可以从const char*隐式构造,因此最后两行可以进一步简化:

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

由于scoped_allocator_adaptor使用向量的分配器自动构造元素,因此这更简单,更易于阅读并且更不易出错.

当向量要求其分配器构造一个元素作为obj的副本时,它将调用:

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

通常,分配器的construct()成员随后将调用类似的内容:

::new (void_ptr) value_type(obj);

但是,如果allocator_typescoped_allocator_adaptor<A>,则它将使用模板元编程来检测是否可以使用适配类型的分配器来构造value_type.如果value_type在其构造函数中未使用分配器,则适配器将执行以下操作:

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

这将调用嵌套分配器的construct()成员,该成员使用类似于new的放置,如上所述.但是,如果对象确实支持在其构造函数中使用分配器,则scoped_allocator_adaptor<A>::construct()会执行以下任一操作:

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

或:

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

即适配器在其嵌套分配器上调用construct()时会传递附加参数,以便使用分配器构造对象. inner_allocator_typescoped_allocator_adaptor的另一个特化,因此,如果元素类型也是容器,则它使用相同的协议来构造 its 元素,并且分配器可以向下传递到每个元素,甚至当您有一个容器的容器等容器时.

所以适配器的目的是包装一个现有的分配器,并执行所有构造函数参数的元编程和操作,以将分配器从容器传播到其子容器.

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?

解决方案

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.

这篇关于std :: scoped_allocator_adaptor的用途是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-09 21:56