std::pmr::polymorphic_allocator is a non-propagating allocator: std::allocator_traits<std::pmr::polymorphic_allocator>::propagate_on_container_move_assignment::value is false and correspondingly, it has its move assignment operator deleted.
Since basic_memory_buffer::operator=(basic_memory_buffer&& other) unconditionally calls
|
FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { |
|
alloc_ = std::move(other.alloc_); |
for both propagating and non-propagating allocators, a snippet like this won't compile:
std::pmr::unsynchronized_pool_resource r1;
using pmr_memory_buffer = fmt::basic_memory_buffer<char,10,std::pmr::polymorphic_allocator<char>>;
pmr_memory_buffer b1{ &r1 };
pmr_memory_buffer b2{ &r1 };
......
b2 = std::move( b1 );
A bit more practical snippet can be found here: Compiler Explorer. It was inspired by the way fmt::memory_buffer class is used in spdlog. Its ringbuffer_sink declares a circular buffer of basically fmt::memory_buffers, and move()ing instances of fmt::memory_buffers into it when they are ready.
I'm considering a fix where the move-propagating allocators will behave as before, whereas those that don't propagate on move will check whether the allocators (and the memory resources) are the same for the source and for the target. If source and target allocator instances are the same, move() can proceed as usual, passing ownership of the underlying buffer. If the allocators are different, the buffer is then copied. That would replicate the behaviour of standard library containers.
std::pmr::polymorphic_allocatoris a non-propagating allocator:std::allocator_traits<std::pmr::polymorphic_allocator>::propagate_on_container_move_assignment::valueisfalseand correspondingly, it has its move assignment operator deleted.Since
basic_memory_buffer::operator=(basic_memory_buffer&& other)unconditionally callsfmt/include/fmt/format.h
Lines 829 to 830 in 40626af
for both propagating and non-propagating allocators, a snippet like this won't compile:
std::pmr::unsynchronized_pool_resource r1; using pmr_memory_buffer = fmt::basic_memory_buffer<char,10,std::pmr::polymorphic_allocator<char>>; pmr_memory_buffer b1{ &r1 }; pmr_memory_buffer b2{ &r1 }; ...... b2 = std::move( b1 );A bit more practical snippet can be found here: Compiler Explorer. It was inspired by the way
fmt::memory_bufferclass is used in spdlog. Itsringbuffer_sinkdeclares a circular buffer of basicallyfmt::memory_buffers, andmove()ing instances offmt::memory_buffers into it when they are ready.I'm considering a fix where the move-propagating allocators will behave as before, whereas those that don't propagate on move will check whether the allocators (and the memory resources) are the same for the source and for the target. If source and target allocator instances are the same,
move()can proceed as usual, passing ownership of the underlying buffer. If the allocators are different, the buffer is then copied. That would replicate the behaviour of standard library containers.