- Існує багато прикладів функцій, які я знаю, що ніколи не кинуть, але для яких компілятор не може визначити це самостійно. Чи слід додати ноекрім декларації функції у всіх таких випадках?
noexcept
є складним, оскільки це частина інтерфейсу функцій. Особливо, якщо ви пишете бібліотеку, код вашого клієнта може залежати від noexcept
властивості. Пізніше це буде важко змінити, оскільки ви можете зламати існуючий код. Це може викликати менше занепокоєння, коли ви реалізуєте код, який використовується тільки у вашій програмі.
Якщо у вас є функція, яка не може кинути, запитайте себе, чи сподобається вона залишитися, noexcept
чи це обмежить майбутні реалізації? Наприклад, ви можете ввести перевірку помилок на незаконні аргументи, викидаючи винятки (наприклад, для одиничних тестів), або ви можете залежати від іншого коду бібліотеки, який міг би змінити його специфікацію виключень. У такому випадку безпечніше бути консервативним і опускати noexcept
.
З іншого боку, якщо ви впевнені, що функція ніколи не повинна кидатись, і це правильно, що вона є частиною специфікації, вам слід оголосити її noexcept
. Однак майте на увазі, що компілятор не зможе виявити порушення, noexcept
якщо ваша реалізація зміниться.
- У яких ситуаціях я повинен бути більш уважним щодо використання noexcept, і в яких ситуаціях я можу піти з мається на увазі noexcept (false)?
Існує чотири класи функцій, на яких слід зосередитись, оскільки вони, ймовірно, матимуть найбільший вплив:
- операції переміщення (оператор присвоєння переміщення та конструктори переміщення)
- операції своп
- оператори пам'яті (оператор видалення, оператор видалення [])
- деструктори (хоча це неявно,
noexcept(true)
якщо ви їх не зробите noexcept(false)
)
Ці функції, як правило, повинні бути noexcept
, і, швидше за все, реалізація бібліотеки може використовувати noexcept
властивість. Наприклад, std::vector
можна використовувати операції з переміщення, що не кидаються, без шкоди для строгих гарантій виключення. В іншому випадку доведеться повернутися до елементів копіювання (як це було в C ++ 98).
Цей вид оптимізації знаходиться на алгоритмічному рівні і не покладається на оптимізацію компілятора. Це може мати значний вплив, особливо якщо елементи копіювати дорого.
- Коли я реально розраховувати спостерігати покращення продуктивності після використання noexcept? Зокрема, наведіть приклад коду, для якого компілятор C ++ здатний генерувати кращий машинний код після додавання noexcept.
Перевага noexcept
перед специфікацією без винятку або в throw()
тому, що стандарт дозволяє компіляторам більше свободи, коли справа стосується розмотування стека. Навіть у throw()
випадку, компілятору доводиться повністю розкручувати стек (і це потрібно робити у точному зворотному порядку конструкцій об'єкта).
У noexcept
випадку, з іншого боку, цього робити не потрібно. Немає вимоги, що стек має бути розкрученим (але компілятору все-таки дозволяється це робити). Ця свобода дозволяє здійснювати подальшу оптимізацію коду, оскільки вона знижує накладні витрати завжди на змогу розмотати стек.
Питання щодо noexcept, розмотування стека та продуктивності містить більш детальну інформацію про накладні витрати, коли потрібно розмотувати стек.
Я також рекомендую книгу Скотта Майєрса "Ефективні сучасні C ++", "Пункт 14: Декларуйте функції, за винятком випадків, якщо вони не випускають винятків" для подальшого читання.
move_if_nothrow
(або щоchamacallit), побачить поліпшення продуктивності, якщо є неісключений ctor переміщення.