Quand dois-je vraiment utiliser noexcept?
  le mot-clé  noexcept  peut être appliqué de façon appropriée à de nombreuses signatures de fonctions, mais je ne sais pas quand je devrais envisager de l'utiliser dans la pratique. Sur la base de ce que j'ai lu jusqu'à présent, l'ajout de dernière minute de  noexcept  semble répondre à certaines questions importantes qui se posent lorsque les constructeurs de mouvements lancent. Cependant, je ne suis toujours pas en mesure de fournir des réponses satisfaisantes à certaines questions pratiques qui m'ont amené à lire plus sur  noexcept  en premier lieu.  
-    
il y a beaucoup d'exemples de fonctions que je sais ne jetteront jamais, mais pour lesquelles le compilateur ne peut pas le déterminer seul. Dois-je ajouter
noexceptà la déclaration de fonction dans tous ces cas?avoir à penser si oui ou non je dois ajouter
noexceptaprès chaque déclaration de fonction réduirait considérablement la productivité des programmeurs (et franchement, serait une douleur dans le cul). Pour quelles situations devrais-je faire plus attention à l'utilisation denoexcept, et pour quelles situations puis-je m'en tirer avec lenoexcept(false)implicite ? -   
Quand puis-je raisonnablement m'attendre à observer une amélioration de la performance 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 denoexcept.Personnellement, je me soucie de
noexcepten raison de la liberté accrue fournie au compilateur d'appliquer en toute sécurité certains types d'optimisations. Est-ce que les compilateurs modernes profitent denoexceptde cette façon? Dans la négative, puis-je m'attendre à ce que certains d'entre eux le fassent dans un proche avenir? 
9 réponses
je pense qu'il est trop tôt pour donner une réponse aux" meilleures pratiques " à cet égard, car il n'y a pas eu assez de temps pour l'utiliser dans la pratique. Si cette question était posée à propos des spécificateurs de jet juste après qu'ils soient sortis alors les réponses seraient très différentes à aujourd'hui.
avoir à penser si oui ou non je dois ajouter
noexceptaprès chaque déclaration de fonction réduirait considérablement la productivité de programmeur (et franchement, serait une douleur).
Eh bien l'utiliser quand il est évident que la fonction ne lancera jamais.
Quand puis-je raisonnablement m'attendre à observer une amélioration de la performance après avoir utilisé
noexcept? [...] Personnellement, je me soucie denoexcepten raison de la liberté accrue fournie au compilateur d'appliquer en toute sécurité certains types d'optimisations.
  il semble que les plus grands gains d'optimisation sont de l'utilisateur les optimisations, pas de compilateur en raison de la possibilité de vérifier  noexcept  et la surcharge. La plupart des compilateurs suivent une méthode de manipulation sans pénalité si vous ne lancez pas d'exception, donc je doute qu'elle change beaucoup (ou quoi que ce soit) au niveau du code machine de votre code, bien que peut-être réduire la taille binaire en supprimant le code de manipulation.  
  en utilisant  noexcept  dans le big 4 (constructeurs, affectation, pas destructeurs car ils sont déjà  noexcept  ) causera probablement les meilleures améliorations en tant que contrôles  noexcept  sont "courantes" dans le code de modèle tel que dans les conteneurs std. Par exemple, std::vector n'utilisera pas le mouvement de votre classe à moins qu'il ne soit marqué noexcept  (ou le compilateur peut en déduire autrement).  
comme je répète sans cesse ces jours-ci: sémantique première .
 ajouter noexcept , noexcept(true) et noexcept(false)  est avant tout une question de sémantique. Il conditionne seulement incidemment un certain nombre d'optimisations possibles.  
 en tant que code de lecture de programmeur, la présence de noexcept est similaire à celle de const : il m'aide à mieux grok ce qui peut ou ne peut pas se produire. Par conséquent, il vaut la peine de dépenser certains de temps à penser à si oui ou non vous savez si la fonction jeter. Pour rappel, n'importe quelle sorte d'allocation de mémoire dynamique peut jeter.  
Ok, maintenant aux optimisations possibles.
  les optimisations les plus évidentes sont effectivement effectuées dans les bibliothèques. C++11 fournit un certain nombre de traits qui permettent de savoir si une fonction est  noexcept  ou non, et L'implémentation de la bibliothèque Standard elle-même utilisera ces traits pour favoriser les opérations  noexcept  sur les objets définis par l'utilisateur qu'ils manipulent, si possible. Telles que    sémantique de déplacement    .  
 le compilateur peut seulement raser un peu de graisse (peut-être) de l'exception traitant des données, parce qu'il  a   pour tenir compte du fait que vous avez peut-être menti. Si une fonction marquée  noexcept lance, alors std::terminate  est appelé.  
cette sémantique était choisi pour deux raisons:
-  bénéficiant immédiatement de  
noexceptmême si les dépendances ne l'utilisent pas déjà (rétrocompatibilité) -   permettant la spécification de  
noexceptlors de l'appel de fonctions qui peuvent théoriquement jeter, mais ne sont pas attendus pour les arguments donnés 
Cela fait en fait une (potentiellement) énorme différence pour l'optimiseur dans le compilateur. Les compilateurs ont en fait cette fonctionnalité depuis des années via l'instruction empty throw() après une définition de fonction, ainsi que des extensions de propriété. Je peux vous assurer que les compilateurs modernes profitent de cette connaissance pour générer un meilleur code.
presque chaque optimisation dans le compilateur utilise quelque chose appelé un "graphique de flux" d'une fonction pour raisonner sur ce qui est juridique. Un graphique de flux se compose de ce qui sont généralement appelés "blocs" de la fonction (zones de code qui ont une entrée simple et une sortie simple) et les bords entre les blocs pour indiquer où le flux peut sauter. Noexcept modifie le diagramme de flux.
Vous avez demandé un exemple précis. Considérez ce code:
void foo(int x) {
    try {
        bar();
        x = 5;
        // other stuff which doesn't modify x, but might throw
    } catch(...) {
        // don't modify x
    }
    baz(x); // or other statement using x
}
  
    le graphique de flux pour cette fonction est différent si  bar  est étiqueté  noexcept  (il n'y a aucun moyen pour l'exécution de sauter entre la fin de  bar  et l'instruction catch). Lorsque étiqueté comme  noexcept , le compilateur est certain que la valeur de x est 5 pendant la fonction baz - le bloc x=5 est dit" dominer "le bloc baz(x) sans le bord de bar()  à la déclaration de capture. Il peut alors faire quelque chose appelé "propagation constante" pour générer du code plus efficace. Ici, si baz est inlined, les déclarations utilisant x peuvent aussi contenir des constantes et alors ce qui était une évaluation d'exécution peut être tourné dans une évaluation de compilation, etc.  
  quoi qu'il en soit, brève réponse:  noexcept  permet au compilateur de générer un graphique de flux plus serré, et le graphique de flux est utilisé pour raisonner sur toutes sortes d'optimisations de compilateurs communs. Pour un compilateur, les annotations utilisateur de cette nature sont impressionnantes. Le compilateur va essayer de comprendre ce truc, mais il ne peut généralement pas (la fonction en question peut être dans un autre fichier objet non visible par le compilateur ou utiliser de façon transitoire une fonction qui n'est pas visible), ou quand il le fait, il y a une exception triviale qui pourrait être jetée dont vous n'êtes même pas conscient, donc il ne peut pas l'étiqueter implicitement comme  noexcept  (allouer de la mémoire pourrait jeter bad_alloc, par exemple).  
   noexcept  peut considérablement améliorer la performance de certaines opérations. Cela ne se produit pas au niveau de la génération du code machine par le compilateur, mais en sélectionnant l'algorithme le plus efficace: comme d'autres l'ont mentionné, vous faites cette sélection en utilisant la fonction  std::move_if_noexcept  . Par exemple, la croissance de  std::vector  (par exemple, quand nous appelons  reserve  ) doit fournir une forte exception-garantie de sécurité. S'il sait que le constructeur de mouvements de  T  ne lance pas, il peut juste élément. Sinon, il doit copier tous les  T  s. Cela a été décrit en détail dans    ce post    .  
Quand puis-je de façon réaliste sauf observer une amélioration de la performance 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.
jamais? N'est jamais un moment? Jamais.
   noexcept  est pour    compilateur    optimisations de performance de la même manière que const  est pour optimisation des performances des compilateurs. C'est, presque jamais.  
   noexcept  est principalement utilisé pour permettre à "vous" de détecter au moment de la compilation si une fonction peut lancer une exception. Rappelez-vous: la plupart des compilateurs n'émettent pas de code spécial pour les exceptions à moins qu'il ne jette quelque chose. Donc  noexcept  n'est pas une question de donner le compilateur conseils sur la façon d'optimiser une fonction tellement comme donnant    vous    conseils sur la façon d'utiliser une fonction.  
  Modèles comme  move_if_noexcept  détecte si le constructeur de déplacement est défini avec  noexcept  et sera de retour un  const&  au lieu de  &&  du type si il ne l'est pas. C'est une façon de dire à déplacer si il est très sûr à le faire.  
  En général, vous devez utiliser  noexcept  quand vous pensez qu'il sera réellement    utile    pour le faire. Code prennent des chemins différents si  is_nothrow_constructible  est vrai pour ce type. Si vous utilisez le code cela fera cela, alors n'hésitez pas à noexcept  les constructeurs appropriés.  
en bref: utilisez-le pour les constructeurs de mouvements et les constructions similaires, mais ne vous sentez pas comme si vous avez à faire des noix avec elle.
- il y a beaucoup d'exemples de fonctions que je sais ne lanceront jamais, mais pour lesquelles le compilateur ne peut pas le déterminer lui-même. Dois-je ajouter noexcependant à la déclaration de fonction dans tous ces cas?
 
   noexcept  est délicat, car il fait partie de l'interface de fonctions. En particulier, si vous écrivez une bibliothèque, votre code client peut dépendre de la propriété  noexcept . Il peut être difficile de changer plus tard, car vous risquez de casser le code existant. Cela pourrait être moins préoccupant lorsque vous mettez en œuvre un code qui n'est utilisé que par votre application.  
  si vous avez une fonction qui ne peut pas lancer, demandez-vous si elle aimera rester  noexcept  ou si cela restreindrait les implémentations futures? Par exemple, vous pourriez vouloir introduire une vérification d'erreur des arguments illégaux en lançant des exceptions (par exemple, pour les tests unitaires), ou vous pourriez dépendre d'un autre code de bibliothèque qui pourrait modifier sa spécification d'exception. Dans ce cas, il est plus sûr d'être conservateur et d'omettre  noexcept  .  
 d'autre part, si vous êtes sûr que la fonction ne devrait jamais jeter et il est correct qu'il fait partie de la spécification, vous devez le déclarer noexcept . Cependant, gardez à l'esprit que le compilateur ne sera pas en mesure de détecter des violations de  noexcept  si votre mise en œuvre 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(faux) implicite?
 
il y a quatre catégories de fonctions sur lesquelles vous devriez vous concentrer parce qu'elles auront probablement le plus grand impact:
- les opérations de déplacement (move opérateur d'affectation et des constructeurs de déplacement)
 - opérations de swap de
 - désallocateurs de mémoire (opérateur Supprimer, opérateur Supprimer[])
 -   destructeurs (bien qu'ils soient implicitement  
noexcept(true)sauf si vous les faitesnoexcept(false)) 
  ces fonctions devraient généralement être  noexcept , et il est très probable que les implémentations de bibliothèques peuvent utiliser la propriété  noexcept . Par exemple, std::vector  peut utiliser des opérations de mouvement sans lancer sans sacrifier de fortes garanties d'exception. Dans le cas contraire, il devra revenir à la copie d'éléments (comme il l'a fait dans C++98).  
ce type d'optimisation est au niveau algorithmique et ne repose pas sur des optimisations de compilateurs. Il peut avoir un impact significatif, surtout si les éléments sont coûteux à copier.
- Quand puis-je raisonnablement m'attendre à observer une amélioration de la performance après avoir utilisé noexcept? En particulier, donner un exemple de code en C++ le compilateur 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 la pile déroulement. Même dans le cas throw() , le compilateur doit complètement décompresser la pile (et il doit le faire dans l'ordre exact inverse des constructions de l'objet).  
 dans l'affaire noexcept , en revanche, elle n'est pas tenue de le faire. Il n'est pas nécessaire que la pile doit être dénoué (mais le compilateur est encore autorisé à le faire). Cette liberté permet une optimisation supplémentaire du code, car elle diminue la charge de toujours être en mesure de décompresser la pile.   
La question connexe à propos de noexcept, le déroulement de pile et de la performance va dans plus de détails à propos de la surcharge lorsque le déroulement de pile est requis.
je recommande également Scott Meyers Livre" efficace moderne C++"," Point 14: déclarer les fonctions noexcept si elles ne seront pas émettre des exceptions " pour une lecture plus approfondie.
De Bjarne mots:
lorsque la résiliation est une réponse acceptable, une exception non justifiée va y arriver parce qu'il se transforme en un appel d'arrivée() (§13.5.2.5). En outre, un spécificateur
noexcept(§13.5.1.1) peut faire que le désir explicite.les systèmes à tolérance de pannes réussis sont à plusieurs niveaux. Chaque niveau fonctionne avec autant d'erreurs que possible sans trop se tortiller et laisse le reste de la hausse des niveau. Les Exceptions appuient ce point de vue. Outre,
terminate()confirme ce point de vue en fournissant une échappatoire si le exception-le mécanisme de manipulation lui-même est corrompu ou s'il a été utilisé de manière incomplète, laissant ainsi les exceptions en suspens. Pareillement,noexceptfournit une évasion simple pour les erreurs où essayer de récupérer semble infaisable.double compute(double x) noexcept; { string s = "Courtney and Anya"; vector<double> tmp(10); // ... }le constructeur vectoriel peut ne pas acquérir mémoire de ses dix doubles et lancez un
std::bad_alloc. Dans ce cas, le programme s'arrête. Il se termine sans condition en invoquantstd::terminate()(§30.4.1.3). Il n'invoque pas les destructeurs des fonctions d'appel. Il est mise en œuvre-défini si les destructeurs à partir de portées entre les Les Termesthrowetnoexcept(par exemple, for s in compute()) sont invoqués. Le le programme est sur le point de se terminer, donc nous ne devrions pas dépendre de objet de toute façon. By en ajoutant un spécificateurnoexcept, nous indiquons que notre le code n'a pas été écrit pour faire face à un lancer.
il y a beaucoup d'exemples de fonctions que je sais ne lanceront jamais, mais pour lesquelles le compilateur ne peut pas le déterminer lui-même. Dois-je ajouter noexcependant à la déclaration de fonction dans tous ces cas?
quand vous dites "je sais qu'ils ne jetteront jamais", vous voulez dire en examinant la mise en œuvre de la fonction vous savez que la fonction ne jettera pas. Je pense que cette approche est à l'intérieur.
  il vaut mieux examiner si une fonction peut jeter exceptions pour faire partie de la    conception    de la fonction: aussi important que la liste d'arguments et si une méthode est un mutateur (...   const ). Déclarer que" cette fonction ne fait jamais d'exception " est une contrainte sur la mise en œuvre. Omettre cela ne signifie pas que la fonction pourrait lancer des exceptions; cela signifie que la version actuelle de la fonction    et    toutes les versions futures peuvent lancer des exceptions. C'est un contrainte qui rend la mise en œuvre plus difficile. Mais certaines méthodes doivent avoir la contrainte d'être pratiquement utiles; plus important encore, de sorte qu'ils peuvent être appelés à partir des destructeurs, mais aussi pour la mise en œuvre du code "roll-back" dans les méthodes qui fournissent la garantie d'exception forte.  
étant donné le fait que le nombre de règles pour travailler witch c++ est en augmentation constante et que nous avons vécu heureux sans exception pendant des années... je pense qu'à moins que quelqu'un donne des raisons de tuer pourquoi l'utiliser, personne ne le fera... ou au moins tous les jours c++ développeurs