分配嵌套 STL 容器时使用哪个分配器

Which allocator is used when allocating a nested STL container?

本文关键字:分配器 嵌套 STL 分配      更新时间:2023-10-16

我有一个关于STL类和分配器的问题,在网上似乎不容易找到。有谁知道嵌套 STL 类中使用了哪个分配器?例如:

typedef std::vector<int> myvect;

下面的行是按照后续回复/评论所指出的编辑的

typedef std::map<int, myvect, std::less<int>, A> mymap; //uses my custom allocator for map creation

让我们调用默认分配器D,并假设我有一个自定义分配器A

如果我执行以下操作会发生什么:

  • 创建地图:

    mymap mapInstance;
    
  • 现在,假设存在一个mapInstance[0]条目,假设我将一个值推入向量:

    mapInstance[0].push_back(999);
    

什么分配器用于矢量mapInstance[0]的动态存储器?

到目前为止,我的理解是使用默认分配器D,但我想确认没有使用传递给地图的自定义分配器A。(据我所知,这只有在我使用某种嵌套分配选项时才会发生。

当然,我知道mapInstance[0]的元数据/标头信息是使用自定义分配器A分配的。我关心的是动态内存部分,即d_dataBegin之后的部分。

您的问题与作用域分配器模型有关,这是一种分配器设计样式,可自动将容器的分配器传播到容器的元素,以便您可以确保容器的所有元素都从同一分配器分配。更多内容见下文。

要回答您的问题:

1) 默认情况下,容器不使用作用域分配器模型,您必须显式请求它(请参阅下面的scoped_allocator_adaptor)

2)您的嵌套容器是类型std::vector<int>这意味着它使用默认的std::allocator<int>分配器,并且该类型的所有实例都是相同的,因此您的问题的答案是它使用标准分配器 - 哪一个并不重要,因为每个std::allocator<int>都是一样的。


这个答案的其余部分只是一个思想实验,你的问题的答案在上面:vector<int>总是使用std::allocator<int>

现在,如果您的嵌套类型std::vector<int, A1<int>>其中A1<int>是自定义分配器,那么问题会变得更加有趣。

嵌套容器将使用它所构造的分配器,而您还没有证明这一点,因为您已经说过">假设存在mapInstance[0]条目",并且该条目的创建方式决定了它将使用哪个分配器。

如果该条目是按如下所示创建的:

mapInstance[0];

则该条目是默认构造的,并将使用默认构造的A1<int>

如果该条目是按如下所示创建的:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

然后条目将是v的副本,在 C++03 中,它的分配器将是v.get_allocator()的副本,但在 C++11 中,它的分配器将是std::allocator_traits<A1<int>>::select_on_container_copy_construction(v.get_allocator())的副本,这可能是v.get_allocator()的副本,但可能是不同的(例如默认构造的A1)。

(在 C++03 中,条目的分配器在创建后无法更改,因此答案将在此处结束,但在 C++11 中可以替换它。我假设我们在这个问题的其余部分谈论C++11,因为分配器在C++03中不是很有趣。

如果该条目按如下所示修改:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance[0] = v;

然后向量被复制分配给,这可能会取代分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_copy_assignment::value的值

如果该条目按如下所示修改:

A1<int> a1( /* args */ );
mapInstance[0] = myvect(a1);

然后向量被移动到,这可能会取代分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_move_assignment::value

如果该条目按如下所示修改:

A1<int> a1( /* args */ );
myvect v(a1)
swap( mapInstance[0], v );

然后交换向量,这可能会取代分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_swap::value的值


现在,如果Astd::scoped_allocator_adaptor<A1<std::pair<const int, myvect>>>事情变得更加有趣! 顾名思义,scoped_allocator_adaptor是一个适配器,它允许任何分配器类型与作用域分配器模型一起使用,这意味着容器的分配器可以传递给容器的子项及其子项的子项等(只要这些类型使用分配器并且可以使用它们构造)。

默认情况下,容器和分配器不使用需要使用的作用域分配器模型scoped_allocator_adaptor(或编写自己的分配器类型,其工作原理相同)来使用它。 (C++03 完全不支持作用域分配器。

如果该条目是按如下所示创建的:

mapInstance[0];

然后,scoped_allocator_adaptor将使用地图分配器的副本构造它,而不是默认构造的条目,因此该条目将像myvect( A1<int>(mapInstance.get_allocator()) )一样构造。

如果该条目是按如下所示创建的:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

然后条目将有v数据的副本,但不会使用其分配器,相反,它将由scoped_allocator_adaptor传递一个分配器,因此将构造为:myvect( v, A1<int>(mapInstance.get_allocator()) )


如果这一切都有点令人困惑,欢迎来到我的世界,但不要担心,在您的情况下,vector<int>将始终使用std::allocator<int>.

默认分配器D用于push_back调用。

事实上,默认分配器也用于map,因为容器的类型指定使用默认分配器。假设您的A继承自std::allocator,所发生的只是您的分配器被切片为默认分配器,并且容器的行为就像您根本没有传递分配器实例一样。