为什么`std :: pmr :: polymorphic_allocator`不会在容器移动时传播?

Quu*_*one 7 c++ allocator c++17

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

polymorphic_allocator不会在容器副本分配,移动分配或交换上传播.因此,移动分配polymorphic_allocator-using容器可以抛出,并且交换两个polymorphic_allocator使用其分配器不会比较相等的容器导致未定义的行为.

为什么我会想要这种行为?这不仅会在swap上引入无偿的未定义行为,更重要的是,对于我的目的,它意味着它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
Run Code Online (Sandbox Code Playgroud)

我的猜测是,这是处理所有权问题的原始尝试.在我上面的第二个例子中,v持有一个引用,mrv实际上并不拥有 mr.允许非拥有引用在整个系统中未经检查地传播将倾向于引入许多微妙的错误.因此,设计人员决定不传播引用,而不是发明拥有的分配器.这最终产生了不良影响,例如移动 - 分配矢量现在复制其数据; 但是你最终还是没有那么多关于内存资源的悬空指针.(有些是,但没有那么多.)mr


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

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
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 8

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);
Run Code Online (Sandbox Code Playgroud)

C++标准要求这不会断言.赋值不会移动容器的分配器 ; 这就是容器赋值在C++中与分配器一起使用的方式.

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

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

在运行时,它实际上可以决定是否可以进行传播.如果可能,那么它将采用更有效的路径.