Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `std::pmr::polymorphic_allocator` not propagate on container move?

From http://en.cppreference.com/w/cpp/memory/polymorphic_allocator:

polymorphic_allocator does not propagate on container copy assignment, move assignment, or swap. As a result, move assignment of a polymorphic_allocator-using container can throw, and swapping two polymorphic_allocator-using containers whose allocators do not compare equal results in undefined behavior.

Why would I ever want this behavior? Not only does this seem to introduce gratuitous undefined behavior on swap, but more importantly for my purposes, it implies that std::pmr::vector is effectively a non-move-assignable type. I mean, it's move-assignable, but that's almost guaranteed to be inefficient.

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

My guess is that this is a primitive attempt to deal with ownership issues. In my second example above, v holds a reference to mr but v does not actually own mr. Allowing non-owning references to propagate unchecked throughout the system would tend to introduce lots of subtle bugs. So rather than invent an owning allocator, the designers decided simply not to propagate the reference to mr. This ended up having bad effects, such as that move-assigning a vector now copies its data; but you don't end up with quite so many dangling pointers to memory resources. (Some yes, but not as many.)


P.S. I do already see that you can avoid the copying/throwing by setting up the allocator in advance, like this:

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
like image 237
Quuxplusone Avatar asked Jul 13 '17 18:07

Quuxplusone


People also ask

What is pmr in c++?

class polymorphic_allocator; (since C++20) The class template std::pmr::polymorphic_allocator is an Allocator which exhibits different allocation behavior depending upon the std::pmr::memory_resource from which it is constructed.

What is namespace PMR?

namespace pmr { template <class T> using vector = std::vector<T, polymorphic_allocator<T>>; } // namespace pmr. The standard library containers (except std::array) are constructed in such a way that the functions for allocating and freeing memory are encapsulated in an allocator which is passed as a template parameter.


1 Answers

The allocator_traits<>::propagate_on_container_copy/move_assignment/swap are static properties of an allocator. A polymorphic allocator, by design, has its properties defined at runtime. Some polymorphic allocators can propagate to others, and some cannot.

Therefore, it is impossible for these properties to be known at compile time. So the PMR allocator must assume the worst case at compile time: no propagation.

Let's take your example, with an alteration:

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);

The C++ standard requires that this does not assert. Assignment does not move the allocator of a container; that's how container assignment works with allocators in C++.

Therefore, either the allocator of the destination container can handle the memory of the source container, or it cannot. And if it cannot, then the destination container must allocate memory and copy/move the objects from the source.

Whether the move assignment operator is noexcept or not is a static property of the operator. And since PMRs cannot know until runtime whether they can propagate storage, the container must designate its move assignment operator as a throwing move assignment.

At runtime, it can actually decide whether propagation is possible or not. And if it is possible, then it will take the more efficient path.

like image 140
Nicol Bolas Avatar answered Oct 21 '22 10:10

Nicol Bolas