Quand dois-je vraiment utiliser noexcept?
La noexcept
mot-clé peut être appliquée à de nombreuses signatures de fonction, mais je ne sais pas quand je devrais envisager de l'utiliser dans la pratique. Basé sur ce que j'ai lu jusqu'à présent, à la dernière minute, plus de noexcept
semble répondre à certaines questions importantes qui se posent lorsque les constructeurs de déplacement jeter. Cependant, je suis toujours incapable de fournir des réponses satisfaisantes à certaines questions pratiques qui m'a conduit à lire plus à propos de noexcept
en premier lieu.
- Il existe de nombreux exemples de fonctions que je connais ne sera jamais jeter, mais pour lesquels le compilateur ne peut pas déterminer si sur son propre. Dois-je ajouter
noexcept
à la déclaration de la fonction dans tous ces cas?Avoir à vous soucier de savoir si ou non j'ai besoin d'ajouter
noexcept
après chaque déclaration de fonction permettrait de réduire considérablement la productivité du programmeur (et franchement, ce serait une douleur dans le cul). Pour les situations qui devrais-je être plus prudent sur l'utilisation denoexcept
, et pour quelles situations puis-je obtenir loin avec l'implicitenoexcept(false)
? - Quand peut-on raisonnablement s'attendre à observer une amélioration de la performance après l'utilisation de
noexcept
? En particulier, donner un exemple de code pour un compilateur C++ est capable de produire la meilleure machine de code après l'ajout denoexcept
.Personnellement, je me soucie de
noexcept
en raison de l'augmentation de la liberté à condition de le compilateur d'appliquer en toute sécurité certains types d'optimisations. Ne les compilateurs modernes profiter denoexcept
de cette façon? Si non, puis-je attendre de certains d'entre eux de le faire dans un avenir proche?
move_if_nothrow
(ou whatchamacallit) va voir une amélioration de la performance si il y a un noexcept déplacer ctor.Connexes: github.com/isocpp/CppCoreGuidelines/blob/master/...
C'est
move_if_noexcept
.OriginalL'auteur void-pointer | 2012-05-28
Vous devez vous connecter pour publier un commentaire.
Je pense qu'il est trop tôt pour donner une "meilleures pratiques" en réponse à cette situation, il n'y a pas eu assez de temps pour l'utiliser dans la pratique. Si cela a été demandé à propos de jeter les spécificateurs de droit après ils sont venus ensuite les réponses seraient très différents de maintenant.
Eh bien l'utiliser quand il est évident que la fonction ne sera jamais jeter.
Il semble que la plus grande optimisation des gains sont de l'utilisateur optimisations, pas de compilateur en raison de la possibilité de vérifier
noexcept
et de surcharge. La plupart des compilateurs suivre sans peine-si-vous-ne pas jeter l'exception de la méthode de gestion donc je doute qu'il allait changer grand chose (ou rien) sur la machine de niveau de code de votre code, mais peut-être réduire la taille du binaire en supprimant le code de traitement.À l'aide de
noexcept
dans le big 4 (constructeurs, de cession, de ne pas les destructeurs car ils sont déjànoexcept
) sera susceptible de provoquer la meilleure des améliorationsnoexcept
contrôles sont "communes" dans le code du modèle comme dans std conteneurs. Par exemple,std::vector
de ne pas utiliser votre classe de déplacement, sauf si c'est marquénoexcept
(ou le compilateur peut en déduire c'est le contraire).std::terminate
truc encore obéit à la Zero-modèle de Coût. C'est, il se trouve que la gamme des instructions à l'intérieur de lanoexcept
fonctions est mappé à l'appelstd::terminate
sithrow
est utilisé à la place de la pile dérouleur. Par conséquent, je doute qu'il n'a plus de généraux que l'exception régulière de suivi.Voir ceci: stackoverflow.com/a/10128180/964135 en Fait, il a juste à être non-jeter, mais la
noexcept
garantit."Si un
noexcept
fonction lance alorsstd::terminate
est appelé, ce qui semble comme il s'agirait d'une petite quantité de surcharge"... Non, cela devrait être mis en œuvre en ne générant pas d'exception tables d'une telle fonction, qui à l'exception répartiteur doit attraper et ensuite renflouer.Gestion des exceptions C++ se fait généralement avec des pas de frais généraux à l'exception de sauter tables de la carte potentiellement jeter l'appel des adresses de site de gestionnaire de points d'entrée. La suppression de ces tables est aussi proche qu'il obtient à la suppression de la gestion des exceptions complètement. La seule différence est la taille du fichier exécutable. Probablement pas la peine de mentionner quoi que ce soit.
"Eh bien l'utiliser quand il est évident que la fonction ne sera jamais jeter." Je suis en désaccord.
noexcept
fait partie de la fonction interface; vous ne devez pas ajouter simplement parce que votre mise n'est pas à jeter. Je ne suis pas sûr de la bonne réponse à cette question, mais je suis tout à fait confiant que la façon dont votre fonction arrive à se comporter aujourd'hui n'a rien à faire avec elle...OriginalL'auteur Pubby
Comme je ne cesse de répéter ces jours-ci: sémantique premier.
Ajoutant
noexcept
,noexcept(true)
etnoexcept(false)
est d'abord et avant tout sur la sémantique. Il seulement d'ailleurs état d'un certain nombre d'optimisations possibles.En tant que programmeur de lecture de code, la présence de
noexcept
est semblable à celle deconst
: il m'aide à mieux analyser ce qui peut ou peut ne pas se produire. Par conséquent, il est intéressant de passer du temps à penser à si oui ou non vous savez si la fonction jeter. Pour rappel, tout type d'allocation dynamique de la mémoire peut jeter.Bon, maintenant, sur les optimisations possibles.
La plus évidente des optimisations sont effectivement réalisées dans les bibliothèques. C++11 fournit un certain nombre de traits qui permet de savoir si une fonction est
noexcept
ou non, et de la Bibliothèque Standard de mise en œuvre eux-mêmes l'utilisation de ces traits à la faveurnoexcept
opérations sur les objets définis par l'utilisateur ils manipulent, si possible. Comme sémantique de déplacement.Le compilateur ne peut raser un peu de graisse (peut-être) de l'exception de la manipulation de données, car il a de prendre en compte le fait que vous pouvez avoir menti. Si une fonction marquée
noexcept
ne jeter, puisstd::terminate
est appelé.Ces sémantique ont été choisis pour deux raisons:
noexcept
même lorsque les dépendances ne l'utilisez pas déjà (compatibilité descendante)noexcept
lors de l'appel de fonctions qui peuvent théoriquement jeter, mais ne sont pas prévu pour les arguments donnésnoexcept
fonctions n'aurais pas besoin de faire quelque chose de spécial, parce que des exceptions qui peuvent survenir déclencherterminate
avant d'arriver à ce niveau. Cela diffère grandement d'avoir à traiter et diffuser unebad_alloc
exception.Oui, il est possible de définir noexcept dans la façon comme vous le suggérez, mais ce serait vraiment inutilisable fonctionnalité. De nombreuses fonctions peuvent jeter si certaines conditions ne sont pas les tenir, et l'on ne pouvait pas les appeler, même si vous savez que les conditions sont remplies. Par exemple, toute fonction qui peut jeter std::invalid_argument.
Un peu tard pour une réponse, mais quand même. Les fonctions marquées noexcept peut appeler d'autres fonctions qui peuvent lancer des, la promesse est que cette fonction ne seront pas émettre une exception, c'est à dire qu'ils ont juste à gérer l'exception d'eux-mêmes!
Je upvoted cette réponse il y a longtemps, mais après avoir lu et réfléchi, j'ai un commentaire/question. "La sémantique de déplacement" est le exemple, j'ai jamais vu quelqu'un donner où
noexcept
est clairement utiles / une bonne idée. Je commence à penser à déplacer la construction, l'assignation de déplacement, et swap sont les seuls cas il y a... Ne vous en connaissez d'autres?Dans la bibliothèque Standard, c'est peut-être le seul, cependant il présente un principe qui peut être réutilisé ailleurs. Une opération de déplacement est une opération qui temporairement mis de l'état dans les "limbes", et seulement quand il est
noexcept
quelqu'un peut-il l'utiliser en toute confiance sur un morceau de données qui pourraient être accessibles par la suite. J'ai pu voir cette idée d'être utilisé ailleurs, mais la bibliothèque Standard est plutôt mince en C++ et il est seulement utilisé pour optimiser les copies d'éléments, je pense.OriginalL'auteur Matthieu M.
Ce fait ne font qu'un (potentiellement) la différence est énorme à l'optimiseur dans le compilateur. Les compilateurs ont, en effet, cette fonction depuis des années par le vide throw() déclaration à l'issue d'une définition de fonction, ainsi que la bienséance extensions. Je peux vous assurer que les compilateurs modernes ne tirer parti de ces connaissances pour générer un code de meilleure qualité.
Presque tous d'optimisation du compilateur utilise ce qu'on appelle un "graphe de flot" d'une fonction de raisonner sur ce qui est légal. Un flux graphique comprend ce que sont généralement appelés "blocs" de la fonction (les zones de code qui ont une seule entrée et une seule sortie) et les bords entre les blocs pour indiquer où le débit peut atteindre. Noexcept modifie le flux graphique.
Vous avez demandé un exemple précis. Considérer ce code:
Le flux graphique de cette fonction est différente si
bar
est marquénoexcept
(il n'y a aucun moyen pour passer l'exécution entre la fin debar
et l'instruction catch). Quand étiqueté commenoexcept
, le compilateur est certain que la valeur de x est de 5 au cours de la baz fonction de x=5 bloc est dit à "dominer" la baz(x) bloc sans le bord debar()
à l'instruction catch. Elle peut alors faire quelque chose appelé "constante de propagation" pour générer du code plus efficace. Ici, si baz est inline, les déclarations à l'aide de x peut également contenir des constantes et puis ce que l'habitude d'être un moteur d'exécution de l'évaluation peut être transformé en un moment de la compilation de l'évaluation, etc.De toute façon, la réponse courte:
noexcept
permet au compilateur de générer un resserrement des flux graphique, ainsi que le graphique de flux est utilisé à raison de toutes sortes de courants optimisations du compilateur. Pour un compilateur, l'utilisateur annotations de cette nature sont impressionnantes. Le compilateur va essayer de comprendre ce genre de choses, mais il peut généralement pas (la fonction en question est peut-être dans un autre fichier objet n'est pas visible pour le compilateur ou transitivement utiliser une fonction qui n'est pas visible), ou quand il le fait il y a quelques trivial exception peut être levée que vous n'êtes même pas conscients de sorte qu'il ne peut pas implicitement l'étiquettenoexcept
(allocation de mémoire peut jeter bad_alloc, par exemple).x = 5
pouvez jeter. Si la partie de latry
bloc servi n'importe quel but le raisonnement ne tiens pas.Je dirais que cela fait une réelle différence dans l'optimisation de fonctions qui contiennent des blocs try/catch. L'exemple que j'ai donné, bien que tiré par les cheveux, n'est pas exhaustive. Le point le plus important est que noexcept (comme le lancer() déclaration devant elle) contribue à la compilation de générer un flux plus petit graphique (moins bords, moins de blocs) qui est une partie fondamentale de nombreuses optimisations il fait ensuite.
Comment peut-compilateur reconnaître que le code peut jeter l'exception? Est et de l'accès à la matrice considérée comme l'exception peut-être?
Si le compilateur ne peut pas voir le corps de la fonction, il peut paraître explicite
throw
états, ou d'autres choses commenew
qui peut jeter. Si le compilateur ne peut pas voir le corps, puis il doit s'appuyer sur la présence ou l'absence denoexcept
. Plaine d'accès au tableau ne produisent généralement pas des exceptions C++ n'a pas de vérification des limites) donc pas, d'accès au tableau ne serait pas seul en cause le compilateur de penser à une fonction lève des exceptions. (Out-of-lié accès UB, ne garantisse pas une exception).doit compter sur la présence ou l'absence de noexcept" ou de la présence de
throw()
en pré-noexcept C++OriginalL'auteur Terry Mahaffey
noexcept
peut considérablement améliorer les performances de certaines opérations. Ce n'est pas au niveau de la génération de code machine par le compilateur, mais en choisissant le plus efficace algorithme: comme d'autres l'ont mentionné, vous n'cette sélection à l'aide de la fonctionstd::move_if_noexcept
. Par exemple, la croissance destd::vector
(par exemple, lorsque nous appelonsreserve
) doivent fournir une solide exception-garantie de sécurité. Si elle sait queT
'constructeur de déplacement ne jette pas, on peut le déplacer chaque élément. Sinon, il faut copier tous lesT
s. Ce qui a été décrit en détail dans ce post.noexcept
(le cas échéant)! Implicitement défini déplacer fonctions membres ontnoexcept
ajoutés automatiquement (le cas échéant).OriginalL'auteur Andrzej
De messagerie unifiée, jamais? N'est jamais un moment? Jamais.
noexcept
est pour compilateur de l'optimisation des performances de la même manière queconst
est pour le compilateur d'optimiser les performances. C'est, presque jamais.noexcept
est principalement utilisé pour permettre le "vous" pour déceler au moment de la compilation si une fonction peut renvoyer une exception. Rappelez-vous: la plupart des compilateurs n'émettent pas de code spécial pour les exceptions, sauf si elle a effectivement jette quelque chose. Doncnoexcept
n'est pas une question de donner le compilateur conseils sur la façon d'optimiser une fonction tellement que de donner vous des conseils sur la façon d'utiliser une fonction.De modèles tels que
move_if_noexcept
détecte si le constructeur de déplacement est défini avecnoexcept
et sera de retour unconst&
au lieu d'un&&
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 effectivement utile de le faire. Code prennent des chemins différents siis_nothrow_constructible
est vrai pour ce type. Si vous êtes en utilisant un code qui permettra de le faire, alors n'hésitez pas ànoexcept
appropriée des constructeurs.En bref: l'utiliser pour déplacer les constructeurs et autres constructions similaires, mais ne vous sentez pas comme vous devez aller de noix avec elle.
move_if_noexcept
ne renvoie pas une copie, il sera de retour une const lvalue-référence plutôt qu'une rvalue-référence. En général, qui sera la cause de l'appelant pour faire une copie au lieu d'un déplacement, maismove_if_noexcept
n'est pas de faire de la copie. Sinon, bonne explication.+1 Jonathan. Le redimensionnement d'un vecteur, par exemple, déplacer les objets au lieu de les copier si le constructeur de déplacement est
noexcept
. Pour que "plus jamais" n'est pas vrai.Je veux dire, le compilateur générer un code de meilleure qualité dans cette situation. L'OP demande un exemple pour lequel le compilateur est capable de générer une plus optimisée de l'application. Cela semble être le cas(même si ce n'est pas un compilateur optimisation).
Le compilateur génère un code de meilleure qualité, car le compilateur est obligé de compiler un différentes codepath. fonctionne parce que
std::vector
est écrit pour forcer le compilateur pour compiler code. Il n'est pas sur le compilateur de détecter quelque chose, c'est sur le code de l'utilisateur de détecter quelque chose.Le truc, c'est que je n'arrive pas à trouver "optimisation du compilateur" dans la citation au début de votre réponse. @ChristianRau dit, le compilateur génère un code plus efficace, il n'a pas d'importance quelle est l'origine de cette optimisation. Après tout, le compilateur est génération d'un code plus efficace, n'est-ce pas? PS: je n'ai jamais dit que c'était une optimisation du compilateur, j'ai même dit "Ce n'est pas une optimisation du compilateur".
OriginalL'auteur Nicol Bolas
noexcept
est délicat, car il fait partie des fonctions de l'interface. En particulier, si vous écrivez une bibliothèque, votre code client peut dépendre de lanoexcept
de la propriété. Il peut être difficile de le changer plus tard, car vous risquez de casser le code existant. Qui pourrait être de moins en moins une préoccupation lorsque vous êtes à la mise en œuvre du code qui est utilisé par votre application.Si vous avez une fonction qui ne peut pas jeter, demandez-vous si il va rester
noexcept
ou serait-ce limiter les futures implémentations? Par exemple, vous pouvez introduire la vérification des erreurs d'illégal arguments en jetant des exceptions (par exemple, pour les tests unitaires), ou vous peut dépendre d'autres bibliothèques de code qui pourrait changer sa spécification d'exception. Dans ce cas, il est plus sûr d'être prudents et de les omettrenoexcept
.D'autre part, si vous êtes convaincus que la fonction ne devrait jamais jeter et il est vrai que cela fait partie du cahier des charges, vous devez le déclarer
noexcept
. Cependant, gardez à l'esprit que le compilateur ne sera pas en mesure de détecter les violations denoexcept
si vos modifications de mise en œuvre.Il existe quatre classes de fonctions qui devraient vous devriez vous concentrer sur parce que ils ont probablement le plus d'impact:
noexcept(true)
à moins que vous leur faitesnoexcept(false)
)Ces fonctions doivent généralement être
noexcept
, et il est plus que probable que la bibliothèque implémentations peuvent faire usage de lanoexcept
de la propriété. Par exemple,std::vector
pouvez utiliser de non-lancement des opérations de déplacement, sans sacrifier la forte exception des garanties. Sinon, il faudra revenir à la copie des éléments (comme il l'a fait en C++98).Ce type d'optimisation est sur l'algorithmique niveau et ne pas compter sur les optimisations du compilateur. Il peut avoir un impact significatif, surtout si les éléments sont coûteux à copier.
L'avantage de
noexcept
contre pas de spécification d'exception outhrow()
est que la norme permet l'compilateurs plus de liberté quand il s'agit de déroulement de pile. Même dans lethrow()
cas, le compilateur a pour vous détendre complètement la pile (et il doit le faire dans l'exact inverse de l'ordre de l'objet des constructions).Dans le
noexcept
cas, d'autre part, il n'est pas nécessaire 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 en outre d'optimisation de code car il réduit la surcharge de toujours être en mesure de vous détendre de la pile.La question connexe sur 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 nécessaire.
Je vous le recommande Scott Meyers livre "Effective C++ Moderne", "Article 14: Déclarer des fonctions noexcept si elles ne produisent pas d'exceptions" pour plus de lecture.
throws
mot-clé au lieu denoexcept
négatif. J'ai juste ne peut pas obtenir une partie de C++ les choix de conception...Ils l'ont nommé
noexcept
parce quethrow
était déjà pris. Simplement mettrethrow
peut être utilisé presque vous les mentionner, sauf qu'ils bâclé la conception de sorte qu'il est devenu presque inutile préjudiciable même. Mais nous sommes coincés avec elle maintenant car il enlever serait une modification de rupture avec peu d'avantages. Doncnoexcept
est fondamentalementthrow_v2
.Comment est
throw
pas utile?lui-même (pour lever des exceptions) est utile, mais "jeter" comme une exception spécificateur a été désapprouvée et en C++17 même supprimé. Pour des raisons pourquoi exception des prescripteurs ne sont pas utiles, voir à cette question: stackoverflow.com/questions/88573/...
Le
throw()
exception spécificateur de ne pas offrir la même garantie quenothrow
?OriginalL'auteur Philipp Claßen
Dans Bjarne mots:
Langage de Programmation C++, 4e Édition" pg. 366
OriginalL'auteur Saurav Sahu
Quand vous dites "je sais que [ils] ne sera jamais jeter", tu veux dire par l'examen de la mise en œuvre de la fonction que vous savez que la fonction va pas les jeter. Je pense que cette approche est à l'intérieur.
Il est préférable d'examiner si une fonction est susceptible de lancer des exceptions à faire partie de la conception de la fonction: aussi important que la liste d'arguments et de savoir si une méthode est un mutateur (...
const
). En déclarant que "cette fonction ne jamais lève des exceptions" est une contrainte sur la mise en œuvre. En omettant cela ne signifie pas que la fonction peut lancer des exceptions; il signifie que la version actuelle de la fonction et toutes les versions futures peuvent lever des exceptions. C'est une contrainte qui fait que la mise en œuvre plus difficile. Mais certaines méthodes doivent avoir la contrainte d'être utiles sur le plan pratique; plus important encore, de sorte qu'ils peuvent être appelés à partir des destructeurs, mais aussi pour la mise en œuvre de "roll-back" de code dans les méthodes qui fournissent le fort exception de garantie.Voir aussi un liés à Java en question.
OriginalL'auteur Raedwald