为什么'std::p mr::p olymorphic_allocator'不会在容器移动时传播?



来自 http://en.cppreference.com/w/cpp/memory/polymorphic_allocator:

polymorphic_allocator不会在容器副本分配、移动分配或交换时传播。因此,polymorphic_allocator-using 容器的移动分配可能会抛出,并交换两个polymorphic_allocator-using 容器,其分配器不会在未定义的行为中比较相等的结果。

我为什么要这种行为?这不仅似乎在交换中引入了无端的未定义行为,而且更重要的是,对于我的目的而言,它意味着std::pmr::vector实际上是一种不可移动可分配的类型。我的意思是,它是可移动分配的,但这几乎可以保证效率低下。

std::vector<int> v = {1, 2, 3};
std::vector<int> w;
w = std::move(v);  // nocopy, nothrow
std::pmr::monotonic_buffer_resource mr(1000);
std::pmr::vector<int> v( {1, 2, 3}, &mr );
std::pmr::vector<int> w;
w = std::move(v);  // yescopy, yesthrow

我的猜测是,这是处理所有权问题的原始尝试。在我上面的第二个示例中,v持有对mr的引用,但v实际上并不拥有mr。允许非拥有引用在整个系统中不受检查地传播往往会引入许多微妙的错误。因此,设计师没有发明拥有分配器,而是决定简单地不传播mr的引用。这最终产生了不良影响,例如移动赋值矢量现在复制其数据;但是你最终不会得到那么多指向内存资源的悬而未决的指针。(有些是的,但没有那么多


附言我已经看到您可以通过提前设置分配器来避免复制/抛出,如下所示:

std::pmr::monotonic_buffer_resource mr(1000);
std::pmr::vector<int> v( {1, 2, 3}, &mr );
std::pmr::vector<int> w(v.get_allocator());
w = std::move(v);  // nocopy, nothrow

allocator_traits<>::propagate_on_container_copy/move_assignment/swap是分配器的静态属性。根据设计,多态分配器在运行时定义了其属性。一些多态分配器可以传播到其他分配器,而有些则不能。

因此,在编译时不可能知道这些属性。因此,PMR 分配器必须在编译时假设最坏的情况:不传播。

让我们以你为例,做一个改动:

std::pmr::monotonic_buffer_resource mr(1000);
std::pmr::vector<int> v( {1, 2, 3}, &mr );
std::pmr::vector<int> w;
auto a1 = w.get_allocator();
w = std::move(v);
assert(w.get_allocator() == a1);

C++标准要求不断言这一点。分配不会移动容器的分配器;这就是容器分配与分配器在C++中的工作方式。

因此,目标容器的分配器可以处理源容器的内存,或者不能。如果不能,则目标容器必须分配内存并从源复制/移动对象。

移动赋值运算符是否noexcept是运算符的静态属性。由于 PMR 在运行时之前无法知道它们是否可以传播存储,因此容器必须将其移动分配运算符指定为抛出移动分配。

在运行时,它实际上可以决定传播是否可能。如果可能的话,它将采取更有效的道路。

最新更新