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

Зачем std::vector"s operator[], front а также back функции-члены не указаны как noexcept?

3 ответа

Решение

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

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

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

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

noexcept и std::vector

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

Используйте конструктор копирования, чтобы развернуть вектор

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

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

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

нет, кроме как двигаться

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

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

Используйте конструктор перемещения, чтобы развернуть вектор

Вот почему std::vector опирается на move constructor быть не исключением.

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


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

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

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

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


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

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

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

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

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

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

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