但是,如果容器从模板参数中内部重新绑定给定的分配器,我如何知道容器使用哪种类型的分配器呢?
在构造函数中始终提供一个Allocator(其中T是容器的value_type)。容器会将其转换为必要的Allocator
,其中U是容器的某些内部数据结构。Allocator需要提供这样的转换构造函数,例如:
template <class T> class allocator {
...
template <class U> allocator(const allocator<U>&);
此外,我了解到C++11现在使用作用域分配器(scoped allocators),其允许重复使用容器的分配器用于其包含的容器。
更准确地说,C++11有一个名为`scoped_allocator_adaptor`的分配器适配器。
template <class OuterAlloc, class... InnerAllocs>
class scoped_allocator_adaptor : public OuterAlloc
{
...
};
从C++11开始:
scoped_allocator_adaptor
类模板是一个分配器模板,它指定了容器要使用的内存资源(外部分配器),并且还指定了要传递给容器中每个元素构造函数的内部分配器资源。该适配器使用一个外部和零个或多个内部分配器类型进行实例化。如果只使用一个分配器类型进行实例化,则内部分配器变为scoped_allocator_adaptor
本身,从而对于容器及其内部的每个元素以及如果元素本身是容器,则递归地对它们的每个元素使用相同的分配器资源。如果使用多个分配器进行实例化,则第一个分配器是容器使用的外部分配器,第二个分配器被传递到容器元素的构造函数中,如果元素本身是容器,则第三个分配器被传递到元素的元素中,以此类推。如果嵌套容器的深度大于分配器数量,则最后一个分配器将重复使用,如单分配器情况下一样。[注意: scoped_allocator_adaptor
派生自外部分配器类型,因此可以在大多数表达式中替换外部分配器类型。 - 注解]
因此,只有当您将scoped_allocator_adaptor
指定为容器的分配器时,才会获得作用域分配器的行为。
启用作用域分配器的容器实现如何与不知道作用域容器的容器大致不同?
关键在于容器现在通过一个名为allocator_traits
的新类处理其分配器,而不是直接处理分配器。并且容器必须对某些操作使用allocator_traits
,例如在容器中构造和销毁value_type
。容器不得直接与分配器交互。
例如,分配器可以提供一个名为construct
的成员,该成员将使用给定的参数在特定地址上构造类型:
template <class T> class Allocator {
...
template<class U, class... Args>
void construct(U* p, Args&&... args);
};
如果分配器没有提供这个成员,
allocator_traits
将提供一个默认实现。无论如何,容器
必须使用这个
construct
函数构造所有的
value_type
,但是通过
allocator_traits
使用它,而不是直接使用
allocator
:
allocator_traits<allocator_type>::construct(the_allocator, *ugly details*);
scoped_allocator_adaptor
提供自定义的
construct
函数,这些函数将被
allocator_traits
转发利用
uses_allocator
特性,并传递正确的分配器给
value_type
构造函数。容器对这些细节一无所知,它只需要知道必须使用
allocator_traits construct
函数构造
value_type
。
容器必须处理更多细节以正确处理有状态的分配器。通过让容器不做任何假设,而是通过
allocator_traits
获取所有属性和行为来处理这些细节。甚至容器也无法假设
pointer
是
T*
。相反,该类型是通过询问
allocator_traits
找到的。
简而言之,要构建C++11容器,请研究
allocator_traits
。然后,当您的客户使用
scoped_allocator_adaptor
时,您可以免费获得作用域分配器行为。