Note: Originially asked by GreenScape as comment.
After reading Why are the swap member functions in STL containers not declared no
Let's start of by digging into the Standard (N3797):
23.2.1p9
General Container Requirements[container.requirements.general]
If
allocator_traits<allocator_type>::propagate_on_container_swap::value
istrue
, then the allocators ofa
andb
shall also be exchanged using an unqalified call to non-memberswap
. Otherwise, they shall not be swapped, and the behavior is undefined unlessa.get_allocator() == b.get_allocator()
.
What is the purpose of propagate_on_container_swap
?
If an Allocator has a typedef named propagate_on_container_swap
that refers to std::true_type
the underlying Allocators of two containers being swapped, will also swap.[1]
If propagate_on_container_swap
is std::false_type
only the data of the two containers will swap, but the allocators will remain in their place.
[1] This means that after a.swap(b)
, a.get_allocator()
will be that which was previously b.get_allocator()
; the allocators has swapped.
What are the implications of stateful Allocators?
The Allocator is not only responsible for allocating memory for the elements within a Standard container, they are also responsible for the deallocation of said elements.
C++03 didn't allow stateful allocators within standard containers, but C++11 mandates that support for such must be present. This means that we could define an allocator that, depending on how it's constructed, acts in a certain way.
If the allocator has propagate_on_container_swap::value
equal to false
the difference in state between the two allocators involved might lead to undefined behavior, since one instance of the Allocator might not be compatible with the data handled by the other.
What might be the problem with stateful allocators if they are not swapped properly?
Let's say we have a MagicAllocator
which either uses malloc
or operator new
to allocate memory, depending on how it's constructed.
If it uses malloc
to allocate memory it must use free
to deallocate it, and in case of operator new
, delete
is required; because of this it must maintain some information saying which of the two it should use.
If we have two std::vector
which both uses MagicAllocator
but with different states (meaning that one uses malloc
and the other operator new
), and we don't swap the allocators upon a.swap(b)
the allocator won't match the memory allocated for the elements in the two vectors after the swap - which in terms means that the wrong free
/delete
might be called upon deallocation.