- Il existe de nombreux exemples de fonctions que je sais ne lanceront jamais, mais pour lesquelles le compilateur ne peut pas le déterminer par lui-même. Dois-je ajouter noexcept à la déclaration de fonction dans tous ces cas?
noexcept
est délicat, car il fait partie de l'interface des fonctions. Surtout, si vous écrivez une bibliothèque, votre code client peut dépendre de la noexcept
propriété. Il peut être difficile de le modifier plus tard, car vous pourriez casser le code existant. Cela peut être moins préoccupant lorsque vous implémentez du code uniquement utilisé par votre application.
Si vous avez une fonction qui ne peut pas être lancée, demandez-vous si elle aimera rester noexcept
ou cela limitera-t-il les futures implémentations? Par exemple, vous souhaiterez peut-être introduire une vérification d'erreur des arguments illégaux en lançant des exceptions (par exemple, pour les tests unitaires), ou vous pouvez dépendre d'un autre code de bibliothèque qui pourrait changer sa spécification d'exception. Dans ce cas, il est plus sûr d'être conservateur et d'omettre noexcept
.
D'un autre côté, si vous êtes sûr que la fonction ne doit jamais lancer et qu'il est correct qu'elle fasse partie de la spécification, vous devez la déclarer noexcept
. Cependant, gardez à l'esprit que le compilateur ne pourra pas détecter les violations de noexcept
si votre implémentation change.
- Pour quelles situations devrais-je faire plus attention à l'utilisation de noexcept et pour quelles situations puis-je m'en tirer avec le noexcept implicite (faux)?
Il existe quatre classes de fonctions sur lesquelles vous devriez vous concentrer, car elles auront probablement le plus grand impact:
- opérations de déplacement (opérateur d'affectation de déplacement et constructeurs de déplacement)
- opérations de swap
- désallocateurs de mémoire (suppression opérateur, suppression opérateur [])
- destructeurs (bien que ceux-ci le soient implicitement
noexcept(true)
sauf si vous les faites noexcept(false)
)
Ces fonctions devraient généralement l'être noexcept
, et il est très probable que les implémentations de bibliothèque puissent utiliser la noexcept
propriété. Par exemple, std::vector
peut utiliser des opérations de déplacement sans lancer sans sacrifier les garanties d'exception fortes. Sinon, il devra se replier sur la copie des éléments (comme il l'a fait en C ++ 98).
Ce type d'optimisation est au niveau algorithmique et ne dépend pas des optimisations du compilateur. Cela peut avoir un impact significatif, surtout si les éléments sont coûteux à copier.
- Quand puis-je m'attendre de manière réaliste à observer une amélioration des performances après avoir utilisé noexcept? En particulier, donnez un exemple de code pour lequel un compilateur C ++ est capable de générer un meilleur code machine après l'ajout de noexcept.
L'avantage de noexcept
contre aucune spécification d'exception ou throw()
est que la norme permet aux compilateurs plus de liberté quand il s'agit de dérouler la pile. Même dans le throw()
cas, le compilateur doit dérouler complètement la pile (et il doit le faire dans l'ordre inverse exact des constructions d'objet).
Dans le noexcept
cas, en revanche, il n'est pas nécessaire de le faire. Il n'est pas nécessaire que la pile soit déroulée (mais le compilateur est toujours autorisé à le faire). Cette liberté permet une optimisation supplémentaire du code car elle réduit la surcharge de toujours pouvoir dérouler la pile.
La question connexe à propos de noexcept, du déroulement de la pile et des performances donne plus de détails sur la surcharge lorsque le déroulement de la pile est requis.
Je recommande également le livre de Scott Meyers "Effective Modern C ++", "Item 14: Declare functions noexcept if they don't emit exceptions" pour une lecture plus approfondie.
move_if_nothrow
(ou whatchamacallit) verra une amélioration des performances s'il y a un ctor de mouvement sans exception.