为类的AllocatorAwareContainer数据成员使用自定义分配器



给定一个非状态自定义分配器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舞蹈,它使已经相对复杂的领域变得复杂。否则,我认为这些概念是可以翻译的。我没有在标准库容器中使用分配器的经验。

使用分配器有两个基本规则,它们在实践中运行良好:
  1. 对象的分配器在构造后不会改变
  2. 每个分配器感知的类型都应该将一个分配器作为构造函数参数传递给所有分配器感知的成员。

第二条规则意味着类的构造函数需要处理它们的所有成员。在成员类型已知的情况下,确定是否需要分配器以及如何构造分配器是相当直接的。当成员类型在某种形式下是泛型的,也就是说,它依赖于某种形式的模板实参时,通常不知道是否需要分配器。

当不清楚成员是否需要接收分配器时,使用bsl时使用的方法是保存包装的成员。更具体地说,成员将持有一个bslalg::ConstructorProxy<T>,它将静态地确定T是否支持构造函数接受分配器并适当地传递分配器。

最新更新