但是,如果容器内部从模板参数重新绑定给定的分配器,我怎么知道容器使用哪种分配器?
始终Allocator<T>
向构造函数提供一个(容器的在哪里T
)value_type
。容器会将其转换Allocator<U>
为必要U
的容器的一些内部数据结构。Allocator
需要提供这样的转换构造函数,例如:
template <class T> class allocator {
...
template <class U> allocator(const allocator<U>&);
此外,我读到 C++11 现在使用范围分配器,它允许将容器的分配器重用于其包含的容器。
好吧,更准确地说,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
派生自外部分配器类型,因此可以在大多数表达式中替换外部分配器类型。——尾注]
因此,如果您将 a 指定scoped_allocator_adaptor
为容器的分配器,您只会获得作用域分配器行为。
启用范围分配器的容器的实现与不知道范围容器的实现有何不同?
关键是容器现在通过一个新的类allocator_traits
来处理它的分配器,而不是直接处理分配器。并且容器必须用于allocator_traits
某些操作,例如value_type
在容器中构造和销毁s。容器不能直接与分配器对话。
例如,分配器可以提供一个被调用的成员construct
,它将使用给定的参数在某个地址构造一个类型:
template <class T> class Allocator {
...
template<class U, class... Args>
void construct(U* p, Args&&... args);
};
如果分配器不提供此成员,allocator_traits
将提供默认实现。无论如何,容器必须value_type
使用这个函数来构造所有的 s construct
,但是通过 来使用它allocator_traits
,而不是allocator
直接使用:
allocator_traits<allocator_type>::construct(the_allocator, *ugly details*);
scoped_allocator_adaptor
提供了自定义construct
函数,这些函数将allocator_traits
利用uses_allocator
特征并将正确的分配器传递给value_type
构造函数。容器仍然对这些细节一无所知。容器只需要知道它必须构造value_type
usingallocator_traits construct
函数。
容器必须处理更多细节才能正确处理有状态分配器。尽管这些细节也是通过让容器不做任何假设而是通过allocator_traits
. 容器甚至不能假设pointer
是T*
. 而是通过询问allocator_traits
它是什么来找到这种类型。
简而言之,要构建 C++11 容器,请研究allocator_traits
. 然后,当您的客户使用scoped_allocator_adaptor
.