pmr · part 05

Move-assigning pmr containers across arenas deep-copies

· english· audience: working-cpp· AI-generated, reviewed by Filip Sajdak

Move assignment is supposed to be the cheap one. dst = std::move(src) steals the source’s buffer, leaves it empty, and touches no elements. For two pmr containers with different resources, that steal is impossible, and the standard quietly does the expensive thing instead.

src holds a hundred ints in one arena; dst uses another. After dst = std::move(src), dst has the hundred elements, and its resource performed one allocation to hold them. A genuine move allocates nothing. This one copied every element into fresh storage.

Why the pointer cannot move

src’s buffer belongs to src’s arena. If dst stole the pointer, dst would later deallocate that memory into its own resource, which never owned it. That is corruption. So when propagate_on_container_move_assignment is false and the two allocators are not equal, move assignment has no choice: it allocates in the destination’s resource and moves the elements across one at a time. The O(1) operation degrades to O(n), silently, on a runtime property (which arena each container happens to hold) that the type system cannot see.

This is the same stickiness as the copy that dropped its allocator, seen from the move side. The allocator identifies an arena, the arena cannot be transferred, so neither can the buffer. Within a single resource, move is still O(1) and everything behaves; across resources it turns into a copy you did not write.

Reach for std::pmr where the arenas are stable and shared, and be deliberate about moving containers between them. The move still compiles. It just does not do what the word promises.


Sources: Arthur O’Dwyer, “A not-so-quick introduction to the C++ allocator model” · cppreference: polymorphic_allocator.