Можно ли иметь типы с операциями перемещения, которые бросают в контейнеры?

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

Причиной этого является то, что если объект может бросать, он будет бросать не из-за копирования или перемещения содержащихся объектов из старого в новый адрес, а для выброса, если ресурс не был получен. Поэтому вся исходная информация все равно должна быть там. Если это так, то компилятор не сможет отменить операции, которые он сделал для восстановления исходного объекта?

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

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

Я упрощаю вещи? Есть ли что-то, что я пропустил, который держит контейнеры от движущихся объектов без конструктора/оператора перемещения без бросания?

Ответ 1

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

Скажем, у вас есть vector из 10 металических элементов перемещения. И vector должен изменить размер. Таким образом, он перемещает 5 объектов в новую память, но 6-й бросок. Ну, это нормально; строительство не удалось, поэтому предположение заключается в том, что значение 6-го объекта в порядке. То есть, независимо от того, какая гарантия исключения этого типа будет, так это то, как все работает.

Но тогда, поскольку движение одного объекта не удалось, vector должен переместить последние 5 объектов обратно в первый массив, так как vector пытается обеспечить надежную гарантию исключения. Это проблема, так как возврат назад сам может потерпеть неудачу.

C++ в целом не имеет действительных ответов, когда сам процесс восстановления самого сбоя терпит неудачу. Вы можете видеть это в исключениях; вы не можете испускать исключение из деструктора, который вызывается во время процесса восстановления из-за сбоя исключения. std::terminate происходит в этом случае.

То же самое касается vector. Если движение назад должно было потерпеть неудачу, у vector нет разумного ответа. Таким образом, если vector не может гарантировать, что восстановление прежнего состояния массива не является noexcept, тогда он будет использовать копирование, поскольку это может обеспечить эту гарантию.

Ответ 2

Прежде всего, я вряд ли могу представить объект, который приобретает ресурсы в операции перемещения. Подумайте об этом - unique_ptr просто передает указатель, не приобретая ничего, то же самое для shared_ptr. string, vector, все контейнеры и т.д. просто крадут указатели на ресурсы, приобретенные ранее в конструкторе по умолчанию или копии. Мне кажется, что бросание из конструктора перемещения похоже на метание от деструктора. Конечно, продолжай, стреляй себе в колено. Но хорошо, я могу согласиться с тем, что исключения из этого существуют.

Поэтому перейдите во вторую точку - при перемещении может произойти момент, когда на самом деле оба объекта (перемещенные из и перемещенные в) являются недопустимыми. И для отката от такой ситуации потребуется дополнительная "волшебная" функция, которую нужно вызвать, чтобы исправить одну из них. Таким образом, кажется, что данные не могут быть восстановлены, поскольку стандарт не определяет такую функцию.