Предположим, у вас есть функция, которая обычно не может завершиться с ошибкой, например:
std::string convert_integer_to_string(int x);
В тарифе это будет кандидатом на noexcept
, Однако реализация, скорее всего, включает в себя динамическое управление памятью, поэтому она всегда может станд :: bad_alloc при распределении памяти с new
оператор.
Рекомендуется ли аннотировать функцию как noexcept?
С практической точки зрения чрезвычайно трудно справляться с ситуациями нехватки памяти разумным способом. Большинство программ просто предполагают, что памяти достаточно. призвание std::terminate
, как это произойдет, если noexcept
функции бросков std::bad_alloc
кажется разумным в этом случае.
Для меня noexcept
это какая-то форма документации. Это обещание, что вы (или оптимизатор) можете смело предполагать, что эта функция никогда не сработает. Если вы программируете приложение, которое не заботится о ситуациях нехватки памяти, это все еще допустимое предположение.
Я думаю, что самая безопасная рекомендация — никогда не использовать noexcept
если std::bad_alloc
исключение может быть брошено. С другой стороны, мне интересно, есть ли преимущества в использовании noexcept
в любом случае, если вы не заботитесь о нехватке памяти (то есть, если std::terminate
все в порядке).
Если функция может вызвать исключение по какой-либо причине, даже если это std::bad_alloc
, вам следует не объявил это как noexcept
, Существует относительно немного функций, которые действительно не могут генерировать исключение, и где оно также имеет значение. Основная потребность в noexcept
Функция позволяет обнаруживать доступные варианты восстановления после ошибки в случае исключения: например, std::vector<T, A>
может использовать конструкцию перемещения при вставке объекта, предполагая, что конструкция перемещения не генерирует. Если конструкция move может быть выброшена, движущиеся объекты нельзя использовать для восстановления исключительной ситуации при реализации безопасных операций с сильными исключениями. Таким образом, если конструкция перемещения может потерпеть неудачу для типа T
экземпляр std::vector<T, A>
не может перемещать объекты, но должен копировать их.
В частности, сделать не использование noexcept
как ложная документация: это нарушение договора, если функция действительно может бросить. Тот факт, что система реагирует с определенным уровнем поведения в случае этого нарушения, не означает, что вы должны воспользоваться этим. … и хотя простые программы, вероятно, не восстановятся и просто умрут, когда не хватит памяти, реальным программам, по крайней мере, понадобится сохранить достаточное состояние, чтобы восстановить беспорядок, который они оставляют после смерти, то есть это неприемлемо для любой функции. принять решение об уничтожении программы (если, конечно, это не задокументировано намерение функции).
Я не уверен, что сильно переживаю из-за нехватки памяти.
Под некоторыми ОС (по крайней мере, Linux) поведение по умолчанию, когда у вас заканчивается память, должно быть уничтожено ОС ( ой убийца). Это происходит, когда вы пишете в память (а не когда выделяете ее), и у вас не будет возможности запустить какой-либо код очистки. Эта функция называется переполнение памяти
Даже если вы получаете информацию о том, что у вас закончилась память, с этими ошибками довольно сложно справиться должным образом: вам нужно быть абсолютно уверенным, что ваш обработчик исключений не выделяет память. Это включает в себя все функции этого обработчика ошибок, вам также необходимо убедиться, что любой универсальный обработчик исключений, который мог быть запущен по пути (например, ведение журнала), не использует память. Лучшее, на что вы обычно можете надеяться, это простая очистка перед закрытием программы.
Обратите внимание, что вы также можете использовать станд :: nothrow проверить результат распределения без использования исключений (то есть, если ваша ОС фактически сообщит вам эту информацию во время выделения). Это может иметь смысл, когда вы делаете большое выделение, которое, по вашему мнению, может потерпеть неудачу. Это также имеет приятное свойство, заключающееся в том, что вместо обработки (потенциально) необработанных исключений вы получите nullptr, который будет довольно легко отлаживать.