Middle+
47
questionbank

Почему семантика перемещения не будет работать, если оператор перемещения не помечен как noexcept?

Получайте ответы на вопросы в реальном времени с Sobes Copilot
Ответ от нейросети sobes.tech

Семантика перемещения предполагает отсутствие исключений, поскольку при перемещении ресурсы (память, дескрипторы) передаются от одного объекта к другому, и исходный объект остается в допустимом, но неопределенном состоянии. Если оператор перемещения сгенерирует исключение в середине процесса перемещения, это может привести к следующему:

  • Исходный объект может быть уже модифицирован (ресурсы частично или полностью переданы).
  • Целевой объект не получил ресурсы полностью или получил частично.
  • В результате оба объекта могут оказаться в некорректном или неконсистентном состоянии, что приведет к утечкам ресурсов или неопределенному поведению программы.

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

Когда std::vector нужно увеличить свою емкость, он выделяет новый буфер памяти и перемещает или копирует существующие элементы в новый буфер. Если оператор перемещающего присваивания для типа элементов помечен как noexcept, std::vector выбирает перемещение. Если перемещение произойдет успешно для всех элементов, старый буфер освобождается. Если один из операторов перемещения бросит исключение в процессе перемещения, std::vector сможет безопасно откатить операцию, освободив новый неполный буфер и оставив старый буфер (и элементы в нем) нетронутым в его предыдущем корректном состоянии. Это обеспечивает strong exception safety guarantee (гарантия сильной безопасности исключений).

Если оператор перемещения не помечен как noexcept, компилятор не может гарантировать, что перемещение не выбросит исключение. В этом случае, чтобы обеспечить strong exception safety guarantee, std::vector выберет копирование вместо перемещения. Это делается потому, что при копировании исключение, возникшее во время копирования одного элемента, не влияет на корректность уже скопированных элементов и оставляет исходные элементы нетронутыми. Откат операции (удаление неполного нового буфера) также безопасен.

Таким образом, отсутствие гарантии noexcept у оператора перемещения вынуждает стандартную библиотеку (и другие части кода, зависящие от этой гарантии) использовать более безопасную, но потенциально менее производительную операцию копирования вместо перемещения, фактически "отключая" использование семантики перемещения в тех местах, где важна strong exception safety guarantee. Семантика перемещения работает только при использовании rvalue-ссылок и вызове перемещающего конструктора/оператора присваивания, и noexcept является ключевым условием для безопасного и предсказуемого использования этой семантики в контексте исключений.