这个问题与其说是个实际问题,不如说是个好奇的问题。
在C++中,许多容器都是"基于节点的"。这意味着对于存储,它们不能真正使用传递给它们的分配器,相反,它们会执行以下操作:
typedef typename A::template rebind<node>::other node_allocator_type
并可能创建一个类似或类似类型的成员:
node_allocator_type node_allocator;
创建一个可以分配节点的分配器。很好,这一切都是有道理的,但我有一个问题,如果容器真的"有分配器意识",支持有状态分配器,那么在复制/移动容器的过程中会发生什么。
节点分配器是否也被复制/移动?这在表面上是有道理的,但当我们使用复制/移动构造函数时,用户可以指定这样的新分配器,会发生什么?
list( const list& other, const Allocator& alloc );
list( list&& other, const Allocator& alloc );
在这种情况下,other
的分配器没有被复制或移动,那么新构建的列表是否会得到一个全新的node_allocator
和它被复制/移动的alloc
?
这显然是一个实现细节,但我很好奇什么是"正确的"以及典型的实现是什么。我还认识到,在C++11之前,有状态分配器并没有得到很好的支持,因此这不是一个问题。
假设复制构造分配器(甚至是有状态分配器(是一种廉价的操作;如果它们有任何相关的资源,则应该通过引用来持有它们。此外,反弹分配器可以通过复制来构建,并且这些分配器与原始分配器相比是相等的;参见Cpp17Allocator要求中的a == b
。
这样做的结果是,容器可以完全自由地存储Allocator
、Allocator::rebind<void>
或Allocator::rebind<node_type>
,并在分配节点、移动时构造、get_allocator()
的(prvalue!(返回等时根据需要在它们之间进行复制转换。例如,通常libstdc++存储Allocator::rebind<node_type>
。