繁体   English   中英

std :: scoped_allocator_adaptor的目的是什么?

[英]What is the purpose of 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!

现在vector的allocator自动用于构造它的元素,即使插入的对象String("hello")String("world")不是用相同的allocator构造的。 由于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 );

通常,allocator的construct()成员会调用类似于:

::new (void_ptr) value_type(obj);

但是,如果allocator_typescoped_allocator_adaptor<A>那么它使用模板元编程来检测value_type是否可以使用自适应类型的分配器构造。 如果value_type在其构造函数中不使用allocator,那么适配器会:

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

这将调用嵌套分配器的construct()成员,它使用像placement 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另一个特化,所以如果元素类型也是一个容器,它使用相同的协议来构造它的元素,并且分配器可以传递给每个元素,即使你有容器容器的容器等。

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

假设你有一个有状态的竞技场分配器Alloc和一个构造函数Alloc(Arena&) ,它允许你的应用程序有一些特殊的性能,并说你使用嵌套的容器层次结构如下:

using InnerCont = std::vector<int, Alloc<int>>;    
using OuterCont = std::vector<InnerCont, std::scoped_allocator_adaptor<Alloc<InnerCont>>>;    

在这里,使用scoped_allocator_adaptor将允许您将用于初始化分配器的arena对象从外部传播到内部容器,如下所示:

auto my_cont = OuterCont{std::scoped_allocator_adaptor(Alloc<InnerCont>{my_arena})};

这样可以实现更大的数据局部性,并且可以为整个容器层次结构预分配一个大内存竞技场my_arena ,而不是仅仅为外部容器提供my_arena ,并且需要在所有内容容器上循环,并为该级别的每个元素设置另一个竞技场。

类模板实际上是一个可变参数模板,它可以精确控制在每种类型的容器层次结构中使用哪种类型的分配器。 据推测,这给复杂的数据结构带来了更好的性能(我必须承认,我似乎在不同级别的行动中看起来不同的分配器,但也许有数百万用户的大型数据中心在这里有一个用例)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM