- Есть много примеров функций, которые, я знаю, никогда не сгенерируют, но для которых компилятор не может определить это самостоятельно. Должен ли я добавить 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, раскручивании стека и производительности более подробно рассматривается в служебной информации, когда требуется раскрутка стека.
Я также рекомендую книгу Скотта Мейерса «Effective Modern C ++», «Пункт 14: Объявляйте функции без исключения, если они не будут генерировать исключения» для дальнейшего чтения.
move_if_nothrow
(или whatchamacallit), увидит улучшение производительности, если есть не только ход ctor.