I am using Howard Hinnant's nice little arena-based allocator, short_alloc.
It struck me that move-assigning from a vector, which has outgrown its arena and is thus allocated on heap, could be done using the usual, fast move-assignment (i.e., grabbing the target's resources). However, this is not the case:
typedef arena<16> arena_type;
typedef short_alloc<int, 16> alloc_type;
typedef std::vector<int, alloc_type> vec_type;
arena_type arena1, arena2;
vec_type vec1(alloc_type(arena1)), vec2(alloc_type(arena2));
vec1.resize(100);
void* data = vec1.data();
vec2 = std::move(vec1);
assert(vec2.data() == data); // fails
As explained in this answer, this is due to the vector's move assignment operator comparing the two allocators (note that propagate_on_container_move_assignment
is std::false_type
). Since the two allocators don't compare equal (because they have different arenas), the target vector needs to allocate memory and move the values one by one.
The desired behaviour is achieved by changing the equality operator to
template <class T1, size_t N1, class T2, size_t N2>
bool operator==(const short_alloc<T1, N1>& x, const short_alloc<T2, N2>& y) noexcept
{
return N1 == N2 && (&x.a_ == &y.a_ || y.a_.on_heap());
}
where on_heap()
checks if the allocator is not using its arena.
This solution looks rather hackish (note for example that equality is not symmetric), can/will I shoot myself in the foot by doing this? Is there a elegant solution?