polymorphic_allocator:何时以及为什么要使用它?

sky*_*ack 87 c++ allocator c++17

是关于cppreference的文档,是工作草案.

我必须承认,我不明白什么是真正的目的,polymorphic_allocator何时/为什么/如何使用它.
例如,pmr::vector具有以下签名:

namespace pmr {
    template <class T>
    using vector = std::vector<T, polymorphic_allocator<T>>;
}
Run Code Online (Sandbox Code Playgroud)

什么是polymorphic_allocator要约?std::pmr::vector对老式的提供有什么好处std::vector?我现在能做什么才能做到现在为止?
分配器的真正目的是什么?我应该何时使用它?

dav*_*mac 71

来自cppreference的选择报价:

此运行时多态性允许使用polymorphic_allocator的对象在运行时使用不同的分配器类型,尽管相同的静态分配器类型

"常规"分配器的问题是它们改变了容器的类型.如果你想要一个vector特定的分配器,你可以使用Allocator模板参数:

auto my_vector = std::vector<int,my_allocator>();
Run Code Online (Sandbox Code Playgroud)

现在的问题是这个向量与具有不同分配器的向量的类型不同.例如,您不能将它传递给需要默认分配器向量的函数,或者将具有不同分配器类型的两个向量分配给同一个变量/指针,例如:

auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error
Run Code Online (Sandbox Code Playgroud)

多态分配器是单个分配器类型,其成员可以通过动态分派而不是通过模板机制来定义分配器行为.这允许您拥有使用特定的自定义分配但仍然是常见类型的容器.

通过为分配器提供以下内容来完成分配器行为的自定义std::memory_resource *:

// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);

// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);

auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
      // my_vector and my_other_vector have same type
Run Code Online (Sandbox Code Playgroud)

正如我所看到的,主要的剩余问题是std::pmr::容器仍然与std::使用默认分配器的等效容器不兼容.您需要在设计与容器一起使用的界面时做出一些决定:

  • 传入的容器可能需要自定义分配吗?
  • 如果是这样,我应该添加一个模板参数(允许任意分配器)还是应该强制使用多态分配器?

模板解决方案允许任何分配器,包括多态分配器,但具有其他缺点(生成的代码大小,编译时间,代码必须暴露在头文件中,可能导致进一步的"类型污染",从而不断向外推送问题).另一方面,多态分配器解决方案规定必须使用多态分配器.这排除了使用std::使用默认分配器的容器,并且可能会影响与遗留代码的接口.

与常规分配器相比,多态分配器确实具有一些较小的成本,例如memory_resource指针的存储开销(最可能忽略不计)和分配的虚拟函数调度的成本.实际上,主要问题可能是缺乏与不使用多态分配器的遗留代码的兼容性.

  • 对于内存资源不依赖于运行时变量的简单情况,一个好的编译器会去虚拟化,你最终会得到一个没有额外成本的多态分配器(除了存储指针,这实际上不是问题)。我认为值得一提。 (7认同)
  • @EuriPinhollow你不能在'std :: vector <X>`和`std :: pmr :: vector <X>`之间`reinterpret_cast`,如果这就是你所要求的. (6认同)
  • 那么,`std::pmr::` 类的二进制布局很可能不同吗? (3认同)

Yak*_*ont 23

polymorphic_allocator是一个自定义分配器,std::function直接函数调用.

它只是让你在容器中使用一个分配器,而不必在声明时决定哪一个.因此,如果您有一个适合多个分配器的情况,您可以使用polymorphic_allocator.

也许您想要隐藏哪个分配器用于简化您的界面,或者您希望能够将其交换出来用于不同的运行时案例.

首先,您需要需要分配器的代码,然后在考虑pmr向量之前,您需要能够交换使用哪个代码.


Max*_*xpm 10

多态分配器的一个缺点是它polymorphic_allocator<T>::pointer总是只是T*. 这意味着您不能将它们与花哨的指针一起使用。如果您想vector在共享内存中放置 a 的元素并通过boost::interprocess::offset_ptrs访问它们,您需要为此使用常规的旧非多态分配器。

因此,尽管多态分配器允许您在不更改容器的静态类型的情况下改变分配行为,但它们限制了分配的内容。

  • 这是一个关键点,也是一个很大的遗憾。Arthur O'Dwyer 的 [走向有意义的奇特指针](https://quuxplusone.github.io/draft/fancy-pointers.html) 论文探索了这一领域,[他的书“掌握 c++17 STL”]( https://books.google.nl/books?id=zJlGDwAAQBAJ&amp;lpg=PA231&amp;ots=O0fQUCYSa-&amp;dq=pmr%20offset_ptr&amp;pg=PA231#v=onepage&amp;q=pmr%20offset_ptr&amp;f=false) (3认同)