Ответ 1
Пусть начнется, врываясь в стандарт (N3797):
23.2.1p9
Общие требования к контейнерам[container.requirements.general]
Если
allocator_traits<allocator_type>::propagate_on_container_swap::value
true
, то распределителиa
иb
также должны быть обменены используя неквалифицированный вызов не-членуswap
. В противном случае они должны не меняются местами, а поведение undefined, если толькоa.get_allocator() == b.get_allocator()
.
Какова цель propagate_on_container_swap
?
Если у Allocator есть typedef с именем propagate_on_container_swap
, который ссылается на std::true_type
, базовые Распределители из двух контейнеров, подлежащих обмену, также будут заменены. [1]
Если propagate_on_container_swap
есть std::false_type
, данные данных двух контейнеров будут меняться, но распределители останутся на своем месте.
[1] Это означает, что после a.swap(b)
a.get_allocator()
будет то, что было ранее b.get_allocator()
; распределители поменялись местами.
Каковы последствия выносливости с учетом состояния?
Allocator не только отвечает за выделение памяти для элементов в стандартном контейнере, но и за освобождение указанных элементов.
С++ 03 не разрешал назначаемые с точки зрения состояния распределители в стандартных контейнерах, но С++ 11 требует, чтобы такая поддержка присутствовала. Это означает, что мы могли бы определить распределитель, который в зависимости от того, как он сконструирован, действует определенным образом.
Если распределитель имеет propagate_on_container_swap::value
, равный false
, разница в состоянии между двумя задействованными распределителями может привести к поведению undefined, поскольку один экземпляр Allocator может быть несовместим с данными, обрабатываемыми другим.
Что может быть проблемой с генераторами с сохранением состояния, если они не правильно заменены?
Скажем, у нас есть MagicAllocator
, который либо использует malloc
, либо operator new
для распределения памяти в зависимости от того, как она была построена.
Если он использует malloc
для выделения памяти, он должен использовать free
для его освобождения, а в случае operator new
требуется delete
; из-за этого он должен поддерживать некоторую информацию, говорящую о том, какой из них должен использовать.
Если у нас есть два std::vector
, которые используют MagicAllocator
, но с разными состояниями (это означает, что один использует malloc
и другой operator new
), и мы не заменяем распределители на a.swap(b)
распределителем не будет соответствовать памяти, выделенной для элементов в двух векторах после swap - что в терминах означает, что неправильный free
/delete
может быть вызван при освобождении.