给定一个非状态自定义分配器A
(例如,竞技场分配器,绑定到一些运行时已知大小的连续内存块)和类S
,包含一个AllocatorAwareContainer类型的字段:
struct S
{
A() = default;
// another c-tors
std::vector< T > v;
std::shared_ptr< U > s;
};
我想将A
用于S
类的子集(甚至所有)AllocatorAwareContainer字段。很明显,我应该为类S
提供另一个模板参数,并相应地更改所有感兴趣的数据成员的类型,如下所示:
template< typename A = std::allocator< void > >
struct S
{
// c-tors
template< typename X >
using allocator = typename std::allocator_traits< A >::template rebind< X >::other;
std::vector< T, allocator< T > > v;
std::shared_ptr< U, allocator< U > > s;
};
我应该对现有构造函数和其他构造函数做哪些更改(假设A
不是DefaultConstructible和任何其他可能的限制)?我应该将分配器A
存储在S
类的附加字段中吗?
为类使用自定义分配器的一般技巧是什么?
我习惯使用bsl
的容器,它是分配器感知的,并创建分配器感知的类。与标准容器及其分配器使用的主要区别在于bsl
使用指向分配器基类(bslma::Allocator
)的指针。这种方法很好地摆脱了std::rebind
舞蹈,它使已经相对复杂的领域变得复杂。否则,我认为这些概念是可以翻译的。我没有在标准库容器中使用分配器的经验。
- 对象的分配器在构造后不会改变。
- 每个分配器感知的类型都应该将一个分配器作为构造函数参数传递给所有分配器感知的成员。
第二条规则意味着类的构造函数需要处理它们的所有成员。在成员类型已知的情况下,确定是否需要分配器以及如何构造分配器是相当直接的。当成员类型在某种形式下是泛型的,也就是说,它依赖于某种形式的模板实参时,通常不知道是否需要分配器。
当不清楚成员是否需要接收分配器时,使用bsl
时使用的方法是保存包装的成员。更具体地说,成员将持有一个bslalg::ConstructorProxy<T>
,它将静态地确定T
是否支持构造函数接受分配器并适当地传递分配器。