如果您想要一个字符串容器并希望对容器及其元素使用相同的分配器(因此它们都被分配在同一个区域中,正如 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 的另一个特化,所以如果元素类型也是一个容器,它使用相同的协议来构造 its 元素,并且分配器可以传递给每个元素,即使你有容器容器等容器。
因此,适配器的目的是包装现有分配器并执行所有元编程和构造函数参数的操作,以将分配器从容器传播到其子级。