Подтвердить что ты не робот

Почему оператор векторного доступа не указан как noexcept?

Почему std::vector operator[], front и back функции-члены не указаны как noexcept?

4b9b3361

Ответ 1

Стандартная политика noexcept заключается в том, чтобы отмечать только те функции, которые не могут или не должны терпеть неудачу, но не те, которые просто указываются не для исключения исключений. Другими словами, все функции, которые имеют ограниченный домен (передают неправильные аргументы и вы получаете поведение undefined), не являются noexcept, даже если они не заданы для throw.

Функции, которые будут отмечены, такие вещи, как swap (не должны прерываться, потому что безопасность исключений часто зависит от этого) и numeric_limits::min (не может быть отказано, возвращает константу примитивного типа).

Причина в том, что разработчики могут захотеть предоставить специальные отладочные версии своих библиотек, которые вызывают различные ситуации поведения undefined, так что тестовые среды могут легко обнаружить ошибку. Например, если вы используете индекс вне привязки с vector::operator[] или вызываете front или back на пустой вектор. Некоторые реализации хотят создать там исключение (которому им разрешено: поскольку это поведение undefined, они могут что-либо делать), но стандартная функция noexcept для этих функций делает это невозможным.

Ответ 2

В качестве бесплатного ответа @SebastianRedl: зачем вам нужно noexcept?

noexcept и std::vector

Как вы могли бы знать, vector имеет свою емкость. Если он заполнен при push_back, он выделит большую память, скопирует (или переместит, если С++ 11) все существующие элементы в новую соединительную линию, а затем добавит новый элемент обратно.

Use copy constructor to expand a vector

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

  • Если при распределении памяти выбрано исключение, вектор находится в исходном состоянии. Это прекрасно, просто повторите выброс исключения и дайте пользователю обработать его.

  • Если во время копирования существующих элементов выбрано исключение, все скопированные элементы будут уничтожены вызовом деструктора, выделенная соединительная линия будет освобождена, а исключение выбрано для обработки кодом пользователя. (1)
    После уничтожения всего, вектор возвращается в исходное состояние. Теперь безопасно бросать исключение, чтобы позволить пользователю обрабатывать его, не утечка какого-либо ресурса.

noexcept и move

Приходите в эпоху С++ 11, у нас есть мощное оружие под названием move. Это позволяет нам украсть ресурсы из неиспользуемых объектов. std::vector будет использовать move, когда ему нужно увеличить (или уменьшить) емкость, если операция move noexcept.

Предположим, что во время перемещения выбрано исключение, предыдущая соединительная линия не такая же, как раньше move: ресурсы украдены, оставляя вектор в разбитом состоянии. Пользователь не может обработать исключение, потому что все находится в недетерминированном состоянии.

Use move constructor to expand a vector

Вот почему std::vector полагается на move constructor как noexcept.

Это демонстрация того, как клиентский код будет полагаться на noexcept как спецификацию интерфейса. Если позже требование noexcept не будет выполнено, любой код будет зависеть от того, будет ли он нарушен.


Почему бы просто не отметить все функции как noexcept?

Короткий ответ: безопасный код исключения трудно записать.

Длинный ответ: noexcept установить строгий предел для разработчиков, которые реализуют интерфейс. Если вы хотите удалить noexcept из интерфейса, клиентский код может быть разбит как приведенный выше векторный пример; но если вы хотите создать интерфейс noexcept, вы можете сделать это в любое время.

Таким образом, только при необходимости отметьте интерфейс как noexcept.


В Going Native 2013 Скотт Майерс говорил о ситуации выше, без noexcept, здравомыслие программы потерпит неудачу.

Я также написал блог об этом: http://xinhuang.github.io/when-to-use-noexcept-and-when-to-not/

Ответ 3

Короче говоря, есть функции, указанные с помощью или без noexcept. Он предназначен, потому что они разные. Принцип: функция с заданием undefined, указанная (например, из-за неправильных аргументов) не должна быть с noexcept.

В этой статье явно указаны эти члены без noexcept. В качестве примеров использовались некоторые члены vector:

Примеры функций с широкими контрактами были бы vector<T>::begin() и vector<T>::at(size_type). Примеры функций, не имеющих широкого контракта, были бы vector<T>::front() и vector<T>::operator[](size_type).

См. этот документ для первоначальной мотивации и подробного обсуждения. Наиболее очевидной реалистичной проблемой здесь является тестируемость.