fill insert() - копировать конструктор и копировать присвоение noexcept status?

  1. Должны ли элементы контейнера STL иметь noexcept конструкторы копирования и операторы копирования-назначения? Пожалуйста, предоставьте ссылку, если это возможно.
  2. Если нет, то каково состояние контейнера STL, когда исключение происходит во время мульти-вставки, например, во время вставки заполнения.

Проблема возникает при попытке написать универсальную оболочку, которая позволяет перехватывать / отклонять изменения. Любая реализация, которую я могу придумать, может изменить семантику нижележащего контейнера, если только он не специализирован для каждого типа контейнера (что на самом деле не вариант).

Например, std::vector имеет заливку:

void insert (iterator position, size_type n, const value_type& val);

Это требует value_type быть одновременно CopyInsertable и CopyAssignable. Обратите внимание, что он не требует, чтобы тип значения был DefaultConstructible.

Правка 3 Сам Страуструп (таблица на стр. 956) указывает, что многоэлементная вставка должна иметь строгую гарантию для всех векторов, дек, списков и карт. Это означает, что полная стандартная операция библиотеки либо выполняется успешно, либо не выполняется атомарно.

Редактировать 4 Однако гарантия применяется только тогда, когда соответствующие операции (в данном случае конструктор копирования) сами по себе не генерируют исключения, что является именно моей проблемой.

Насколько я понимаю, это оставляет два основных метода реализации:

  1. Создание фиктивных записей для новых элементов и копирование-назначение val, Это работает только тогда, когда возможно создать фиктивные элементы путем копирования существующих элементов в контейнере или когда value_type является DefaultConstructible (что не является обязательным).
  2. Скопируйте и создайте элементы один за другим непосредственно в их соответствующих местах в контейнере. Кажется, это более или менее каноническая реализация.

Редактировать 2: Я не буду называть это неопределенным поведением, потому что этот термин, кажется, сигнализирует людям о том, что они думают как неопределенные в зависимости от времени выполнения языка / стандарта.

Кажется, что обе реализации оставляют контейнер с неизвестным содержимым (т. Е. Неясно, какие элементы контейнер содержит после исключения), когда конструктор копирования или оператор копирования-назначения вызывают исключение.

Редактировать 1: Обратите внимание, что это не означает, что я предполагаю, что существует плохое поведение по отношению к среде выполнения C++, например, утечки памяти или неопределенные значения. Однако, кажется, более или менее не определено, каково содержимое контейнера. В частности, содержимое контейнера могло быть полностью (хотя и последовательно) изменено.

В качестве примера рассмотрим третий (гибридный) метод:

  1. создать список n копии объекта шаблона val,
  2. скопировать-назначить элементы из этого списка в целевой контейнер.

Разница заключается в влиянии на контейнер, когда конструктор копирования вызывает исключение. В этом случае содержимое контейнера остается неизменным, если выбрасывает конструктор копирования (но все же вызывает неуказанное содержимое, когда оператор присваивания копии выбрасывает). При использовании указателей (т.е. когда не используется std::vector), присвоение копии, вероятно, может быть опущено, и только указатели переставляются, что делает операцию атомарной по отношению к. исключения.

Что касается noexcept на элементах контейнера: объекты создаются с помощью allocator_traits<value_type>::construct(ptr, args)что не noexcept и я также не могу найти требование, чтобы элементы контейнера наиболее noexcept конструктор копирования / оператор копирования-копирования (например, std::shared_ptr а также std::unique_ptr требуют этого).

Обратите внимание, что автоматически генерируемые операции для копирования-конструирования и копирования-назначения должны быть noexcept если они не обязаны вызывать операцию, которая сама может вызвать исключение.

Это оставляет меня в замешательстве, и я уверен, что что-то упустил, но я не могу понять ту часть стандарта, которая может доказать, что я неправ.

1 ответ

Решение
  1. Должны ли элементы контейнера STL иметь кроме конструкторов копирования и операторов назначения копирования? Пожалуйста, предоставьте ссылку, если это возможно.

Нет, нет, и я не могу предоставить ссылку на требование, которого нет, потому что оно отсутствует.

Если бы это было необходимо, в стандарте так и было бы сказано, но это не так.

В общем, элементы даже не обязаны иметь конструктор копирования вообще, для большинства операций достаточно конструктора перемещения. Аналогично для копирования.

  1. Если нет, то каково состояние контейнера STL, когда исключение происходит во время мульти-вставки, например, во время вставки заполнения.

Это зависит от контейнера и от того, где он находится.

Для контейнеров на основе узлов, таких как списки, если выбрасывает одна вставка, все уже вставленные элементы остаются в контейнере.

За std::vector что именно произойдет, зависит от того, в какой вектор вы вставляете и нужно ли перераспределение, и есть ли у элементов конструктор перемещения noexcept, или нет. Все, на что вы можете положиться, это то, что не будет никаких утечек и частично построенных элементов, поэтому вектор находится в допустимом состоянии.

Кажется, что обе реализации оставляют контейнер в неопределенном состоянии (то есть неясно, какие элементы контейнер содержит после исключения), когда конструктор копирования или оператор копирования-назначения вызывают исключение.

Это не неопределенное состояние, это просто состояние, о котором у вас нет полной информации. Ты можешь использовать vector::size() а также vector::capacity() определить его состояние и осмотреть элементы на месте [0, size()) проверить состояние элементов. После этого вы знаете все о состоянии вектора.

т. е. вектор всегда находится в допустимом состоянии, вы просто не знаете достаточно информации, чтобы точно описать это состояние, пока не проверите его.

Слово "неопределенный" предполагает, что состояние является непоследовательным или непознаваемым, что не соответствует действительности. Стандартные контейнеры всегда дают основную гарантию безопасности исключений, поэтому неудачные операции не оставят их в неопределенном состоянии.

Даже в крайнем случае, например vector::push_back() где тип элемента не подлежит копированию и имеет конструктор броска, исключение оставит вектор в "неопределенном" состоянии, поэтому нет утечек, недопустимых объектов и неопределенного поведения.

В качестве примера рассмотрим третий (гибридный) метод:

  • создать список из n копий объекта шаблона val.
  • скопировать-назначить элементы из этого списка в целевой контейнер.

Разница заключается в влиянии на контейнер, когда конструктор копирования вызывает исключение. В этом случае содержимое контейнера остается неизменным, если выбрасывает оператор присвоения копии.

Может быть, я неправильно понимаю, но я не вижу, как это лучше, чем "Копировать-конструировать элементы один за другим прямо в соответствующие места в контейнере". Он выполняет значительно больше работы, выполняя N конструкций копирования и N назначений копирования вместо N конструкций копирования, и я не вижу никаких преимуществ с точки зрения конечного состояния контейнера.

В обоих случаях нужно добавить n новые элементы в контейнере, которые могут быть выброшены, и если он выбрасывает, я не понимаю, почему это имеет какое-либо значение для конечного состояния, планировали ли вы также впоследствии выполнить некоторые дополнительные задания.

Другие вопросы по тегам