Мне интересно, какая именно гарантия безопасности для std::vector::insert
? Меня интересуют как один аргумент, так и перегрузка диапазона этой функции.
Какая гарантия безопасности для вектора:: insert?
Ответ 1
Точная гарантия приведена в С++ 11 23.3.6.5:
Если исключение выбрано иначе, чем конструктор копирования, переместите конструктор, оператор присваивания или оператор присваивания перемещения
T
или любой из операцийInputIterator
, никаких эффектов нет. Если исключение вызывается конструктором перемещения неCopyInsertable
T
, эффекты не заданы.
Ответ 2
Как правило, одноэлементная форма insert
имеет сильную гарантию исключения для любого контейнера в соответствии с [container.requirements.general]/10, но vector::insert
является исключением из этого правила:
[vector.modifier]/1 применяется к vector::insert
; InputIterator
здесь относится к перегрузке insert
, которая вставляет диапазон.
Если исключение генерируется иначе, чем конструктор копирования, переместите конструктор, оператор присваивания или оператор присваивания перемещения
T
или любой из операцийInputIterator
, никаких эффектов нет. Если исключение выбрано конструктором move неCopyInsertable
T
, последствия не заданы.
vector
является контейнером, поддерживающим распределитель, что означает, что он использует распределитель для распределения памяти и построения его элементов [container.requirements.general]/3
Для компонентов, затронутых этим подпунктом, объявляющим
allocator_type
, объекты, хранящиеся в этих компонентах, должны быть построены с использованием функцииallocator_traits<allocator_type>::construct
и уничтожены с помощью функцииallocator_traits<allocator_type>::destroy
. Эти функции вызываются только для типа элемента контейнера, а не для внутренних типов, используемых контейнером.
Я думаю, это означает, что локальные объекты, которые не являются элементами контейнера, могут быть созданы без использования распределителя (например, для копирования и замены). В противном случае требования к ctors типа значения были бы бессмысленными; функция распределителя construct
может иметь разные гарантии исключения, чем тип значения ctor.
CopyInsertable
указан в [container.requirements.general]/13, требуя, чтобы
allocator_traits<A>::construct(m, p, v);
хорошо сформирован; где A
- тип распределителя, m
имеет тип A
, p
является указателем на T
, v
является выражением типа (const
) T
и T
- тип значения контейнера. Это встроенная конструкция из одного аргумента (копирование или перемещение).
Аналогично, указывается MoveConstructible
, но v
является (всегда) значением r типа T
. EmplaceConstructible
следует за тем же самым формаем для нуля или более аргументов вместо v
.
Функция insert
для контейнеров последовательностей предъявляет различные требования к типу значений для различных форм [sequence.reqmts]; здесь включая дополнительные требования для vector
:
- для одного аргумента, который является lvalue типа (
const
)T
, а для формы вставить N копий,T
должно бытьCopyInsertable
иCopyAssignable
- для одного аргумента, являющегося значением r
T
,T
должно бытьMoveInsertable
иMoveAssignable
- для формы диапазона,
T
должен бытьEmplaceConstructible
от разыменованных итераторов (*); кроме того,MoveInsertable
иMoveAssignable
, если итераторы диапазона не являются итераторами вперед
(*) Примечание: EmplaceConstructible
только из разыменованных итераторов недостаточно, если контейнер необходимо изменить для вставки (и, например, *i
для такого итератора не относится к типу значения). Возможно, что спецификация требует, чтобы форма диапазона наследовала требования одноэлементной формы, то есть либо MoveAssignable
, либо CopyAssignable
.
Боковое примечание: Форма диапазона insert
требует, чтобы два итератора не указывали в контейнере, в который они должны быть вставлены.
Я интерпретирую спецификации исключений следующим образом:
Дополнительный оператор CopyInsertable
в спецификации исключения vector::insert
, вероятно, различает базовую гарантию и не гарантирует: dtor контейнера обычно требуется вызвать dtor всех элементов и освободить всю память (в общие требования к контейнерам). То есть, если поведение не определено /undefined, основная гарантия сохраняется.
Почему существует требование, которое объединяет CopyInsertable
и move-ctor (вместо allocator::construct
с rvalue), я не знаю. Перемещение-ctor используется только для объектов, которые не являются элементами контейнера (косвенно, возможно, через allocator::construct
).
Другие замечания о "отсутствии эффектов" (- > сильная гарантия) не применяются к операциям распределителя (construct
). Очевидно, что операции распределителя не должны быть исключены. Поскольку вы можете предоставить нестандартный распределитель, который не использует тип значения copy- и move-ctor для construct
, insert
должен обеспечить надежные исключения даже для операций распределителя construct
. Например, если распределитель construct
не является лишним, во время изменения размера элементы не могут быть построены по ходу, а должны быть скопированы.
Перемещение и назначение копии должны быть исключены из-за сильной гарантии, поскольку элементы, возможно, придется перемещать на insert
; copy-and move-ctor, возможно, не должно быть исключением из-за сильной гарантии из-за локальных объектов, созданных в алгоритме.
Ответ 3
Если метод insert
вставляет один элемент в конец списка и не требует выделения какой-либо памяти, он предлагает надежная гарантия исключения.
Если он должен добавить несколько элементов или должен выделить память, он предлагает базовую гарантию исключения. Boost содержит хорошее описание гарантий исключения.
Основная гарантия: сохранение инвариантов компонента и утечка ресурсов. Сильная гарантия: операция успешно завершилась или выбрала исключение, оставив состояние программы точно так, как было до начала операции. "Без броска" гарантирует, что операция не будет генерировать исключение.
Это означает, что после исключения вы знаете, что vector
будет использоваться, но он может не иметь всех данных, которые вы вставили в него. Все объекты, которые были успешно вставлены, будут полностью построены.