- Есть много примеров функций, которые, как я знаю, никогда не будут выбраны, но для которых компилятор не может определить это самостоятельно. Должен ли я добавлять noexcept к объявлению функции во всех таких случаях?
noexcept
сложно, так как это часть интерфейса функций. В частности, если вы пишете библиотеку, ваш клиентский код может зависеть от свойства noexcept
. Это может быть сложно изменить позже, так как вы можете сломать существующий код. Это может не вызывать беспокойства, если вы реализуете код, который используется только вашим приложением.
Если у вас есть функция, которая не может генерировать вызовы, спросите себя, понравится ли ей оставаться noexcept
или это ограничит будущие реализации? Например, вы можете захотеть ввести проверку ошибок недопустимых аргументов, выбрасывая исключения (например, для модульных тестов), или вы можете зависеть от кода другой библиотеки, который может изменить его спецификацию исключения. В этом случае безопаснее быть консервативным и опустить noexcept
.
С другой стороны, если вы уверены, что функция никогда не должна бросать, и правильно, что она является частью спецификации, вы должны объявить ее noexcept
. Однако имейте в виду, что компилятор не сможет обнаружить нарушения noexcept
, если ваша реализация изменится.
- В каких ситуациях я должен быть более осторожным с использованием noexcept, и в каких ситуациях я могу избежать подразумеваемого noexcept (false)?
Есть четыре класса функций, на которых вам следует сосредоточиться, потому что они, вероятно, будут иметь наибольшее влияние:
- операции перемещения (оператор присваивания перемещения и конструкторы перемещения)
- своп операции
- средства освобождения памяти (оператор delete, оператор delete [])
- деструкторы (хотя они неявно
noexcept(true)
, если вы не сделаете их noexcept(false)
)
Эти функции обычно должны быть noexcept
, и наиболее вероятно, что реализации библиотеки могут использовать свойство noexcept
. Например, std::vector
может использовать операции перемещения без метания без ущерба для строгих гарантий исключений. В противном случае придется вернуться к копированию элементов (как это было в C ++ 98).
Этот вид оптимизации находится на алгоритмическом уровне и не зависит от оптимизации компилятора. Это может иметь значительное влияние, особенно если копирование элементов стоит дорого.
- Когда я могу реально ожидать улучшения производительности после использования noexcept? В частности, приведите пример кода, для которого компилятор C ++ может генерировать лучший машинный код после добавления noexcept.
Преимущество noexcept
перед указанием отсутствия исключений или throw()
состоит в том, что стандарт дает компиляторам больше свободы, когда дело доходит до раскрутки стека. Даже в случае throw()
компилятор должен полностью раскрутить стек (и он должен делать это в точном обратном порядке построения объектов).
В случае noexcept
, с другой стороны, этого делать не нужно. Нет необходимости раскручивать стек (но компилятору все равно разрешено это делать). Эта свобода позволяет дальнейшую оптимизацию кода, поскольку снижает накладные расходы, связанные с возможностью всегда раскручивать стек.
Связанный с этим вопрос о noexcept, разматывании стека и производительности содержит более подробную информацию о накладных расходах, когда требуется размотка стека.
Я также рекомендую книгу Скотта Мейерса «Эффективный современный C ++», «Правило 14: объявляйте функции без исключения, если они не будут генерировать исключения» для дальнейшего чтения.
person
Philipp Claßen
schedule
25.12.2014
move_if_nothrow
(или whatchamacallit), увидит улучшение производительности, если не будет другого аргумента перемещения. - person R. Martinho Fernandes   schedule 28.05.2012