std::scoped_allocater_aptor的用途是什么
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!
现在,向量的分配器会自动用于构造其元素,即使插入的对象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_type
是scoped_allocator_adaptor<A>
,则它使用模板元编程来检测value_type
是否可以用适配类型的分配器来构造。如果value_type
在其构造函数中不使用分配器,那么适配器会使用:
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_type
是scoped_allocator_adaptor
的另一个特殊化,因此,如果元素类型也是一个容器,它会使用相同的协议来构造其元素,并且分配器可以传递到每个元素,即使您有容器的容器等。
因此,适配器的目的是包装现有的分配器,并执行所有元编程和构造函数参数的操作,以将分配器从容器传播到其子级。
假设您有一个带构造函数Alloc(Arena&)
的有状态竞技场分配器Alloc
,它允许您的应用程序具有一些特殊性能,并假设您使用一个嵌套的容器层次结构,如下所示:
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
可用于外部容器,并且需要在所有内部容器上循环,该级别的每个元素都有另一个区域。
类模板实际上是一个可变模板,它使您能够细粒度地控制在每种类型的容器层次结构中使用哪种类型的分配器。据推测,这会给复杂的数据结构带来更好的性能(我必须承认,我似乎没有在任何地方使用不同级别的分配器,但可能拥有数百万用户的大型数据中心在这里有一个用例)。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- C++中名称篡改的目的是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 是什么阻止DOMTimerCoordinator::NextID进入无休止的循环
- 派生类销毁的最佳实践是什么
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 通过JNI传递数据数组的最快方法是什么
- "using namespace std;"在C++的作用是什么?
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 文件系统:复制功能的速度秘诀是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么