Quel est le point de noreturn?
[dcl.attr.noreturn] donne l'exemple suivant:
[[ noreturn ]] void f() {
throw "error";
//OK
}
mais je ne comprends pas quel est le point de [[noreturn]]
, parce que le type de retour de la fonction est déjà void
.
Alors, quel est le point de la noreturn
attribut? Comment est-il censé être utilisé?
- Ce qui est important à propos de ce genre de d'une fonction (qui ont le plus de chances de se produire une fois dans un programme d'exécution) qui mérite une telle attention? N'est-ce pas facilement détectable situation?
- L'OP est l'amalgame entre les notions de “retour” et “valeur de retour”. Compte tenu de la façon dont ils sont presque toujours utilisé en tandem, je pense que la confusion est justifiée.
- Votre titre est parfait!
Vous devez vous connecter pour publier un commentaire.
La noreturn attribut est censé être utilisé pour les fonctions qui ne retournent pas à l'appelant. Cela ne veut pas dire nulle (fonctions qui ne retournent pas à l'appelant - ils juste ne retourne pas une valeur), mais les fonctions où le flux de contrôle ne sera pas de retour à la fonction appelante après la fonction se termine (par exemple, les fonctions que vous quittez l'application, boucle pour toujours ou de lancer des exceptions comme dans votre exemple).
Cela peut être utilisé par les compilateurs de faire quelques optimisations et de générer de meilleurs avertissements. Par exemple, si
f
a la noreturn attribut, le compilateur pourrait vous avertir surg()
mort de code lorsque vous écrivezf(); g();
. De même, le compilateur ne sais pas pour vous avertir sur le manque de retour des déclarations après les appels àf()
.execve
que ne pas de retour, mais pourrait? Devrait-il avoir le noreturn attribut?noreturn
attribut.noreturn
ne peut être utilisée que si votre fonction est la garantie de faire quelque chose qui met fin au programme avant de flux de contrôle peut retourner à l'appelant, par exemple parce que vous appelez de sortie (de), abort(), assert(0), etc.catch
es en dehors de lanoreturn
fonction, ou est en train de lancer des exceptions à l'intérieur de lanoreturn
fonction refusé?noreturn
. La manipulation de cette exception n'est pas la même que celle ayant retourné. Le code à l'intérieur de latry
après l'appel est toujours inaccessible, et si pasvoid
puis de toute cession ou l'utilisation de la valeur de retour ne se fera pas.return
; toutefois, j'ai demandé si il était possible de faire un retour de l'exécution de l'opération. C'est pourquoi j'ai dit “le retour via exception jeter (façon de parler)”. J'aurais probablement écrit “l'accomplissement (hacky) fonctionnalité semblable à revenir à l'aide dethrow
” ou tout simplement “sortie de l'exécution de la fonction et de continuer dans la enfermant appel à l'aide dethrow
”. Tout le monde ici sait quethrow
n'a pas de valeur de retour, et qu'il reprend l'exécution à lacatch
. J'ai demandé pour le retour-comme capacité, pasreturn
.throw
ing était un type dereturn
ing. Je suis conscient que ce n'était pas votre intention, mais c'est pourquoi il vaut la peine explicitement soulignée, même si tout le monde qui a commenté susceptibles de connaître la différence. (Aussi, désolé pour le nécro, mais personne d'autre n'explique donc j'ai senti comme si quelqu'un doit.)throw
est traitée. Alors qu'il est déjà implicite, cela le rend plus clair et coûte très peu à ajouter les phrases "throw
ing à partir d'un[[noreturn]]
fonction est possible, et fonctionne comme prévu -- la seule chose que vous ne pouvez pas faire est dereturn
. Si vous encapsuler l'appel à untry
/catch
, vous pouvez le traiter comme n'importe quel autre lancer la fonction". (Probablement mieux écrit, cependant).noreturn
ne pas dire au compilateur que la fonction ne retourne aucune valeur. Il indique au compilateur que flux de contrôle ne sera pas de retour à l'appelant. Cela permet au compilateur de faire une variété d'optimisations -- il n'a pas besoin d'enregistrer et de restaurer tous les volatiles de l'état autour de l'appel, il peut dead-code d'éliminer n'importe quel code qui serait autrement suivent l'appel, etc.Cela signifie que la fonction ne sera pas complète. Le flux de contrôle n'atteindra jamais l'instruction suivant l'appel à
f()
:L'information peut être utilisée par le compilateur/optimiseur de différentes manières. Le compilateur peut ajouter un avertissement que le code ci-dessus est inaccessible, et il peut modifier le code de
g()
de différentes manières, par exemple à l'appui de poursuites.-Wno-return
et vous recevrez un avertissement. Probablement pas celui que vous attendiez, mais il est probablement suffisant pour vous dire que le compilateur a connaissance de ce que[[noreturn]]
est et il peut prendre avantage de cela. (Je suis un peu surpris de voir que-Wunreachable-code
n'a pas un coup de pied dans...)-Wmissing-noreturn
, l'avertissement implique que l'analyse des flux déterminé que lestd::cout
n'est pas accessible. Je n'ai pas un assez nouveau gcc à la main à l'assembly généré, mais je ne serais pas surpris si l'appel àoperator<<
a été abandonné[[noreturn]]
doit être appliquée de manière transitive vers l'extérieur-plus de portée)-O1
est déjà assez pour déplacer ce code inaccessible sans la[[noreturn]]
soupçon.[[noreturn]]
à partir du code. Si cette unité de traduction n'avait qu'une déclaration de la fonction qui a été défini quelque part d'autre, le compilateur ne sera pas en mesure de déposer le code, car il n' savoir que la fonction ne retourne pas. C'est là que l'attribut doit aider le compilateur.Réponses précédentes correctement expliqué ce que noreturn est, mais pas pourquoi il existe. Je ne pense pas que le "optimisation" des commentaires est le but principal: Fonctions qui ne sont pas de retour sont rares et habituellement n'ont pas besoin d'être optimisé. Au contraire, je pense que la principale raison d'être de noreturn est pour éviter les faux-positifs avertissements. Considérons par exemple ce code:
Avait abort() pas été marqués comme "noreturn", le compilateur peut ont mis en garde à propos de ce code ayant un chemin où f ne pas renvoyer un nombre entier comme prévu. Mais parce que abort() est marqué pas de retour, il sait que le code est correct.
Type théoriquement parlant,
void
est ce qui est appelé dans d'autres languesunit
outop
. Son équivalent logique est Vrai. Toute valeur peut être légitimement exprimées àvoid
(chaque type est un sous-type devoid
). Penser à cela comme une "univers" ensemble; il n'y a pas d'opérations en commun à tous les valeurs dans le monde, donc il n'y a pas d'opérations valides sur une valeur de typevoid
. Mettre une autre manière, vous dire que quelque chose appartient à l'univers vous donne aucune information que ce soit, vous le savez déjà. Donc la suite est du son:Mais la cession ci-dessous n'est pas:
[[noreturn]]
, d'autre part, on appelle parfoisempty
,Nothing
,Bottom
ouBot
et est l'équivalent logique de Faux. Il n'a pas de valeurs à tous, et une expression de ce type peuvent être exprimés à l' (j'.e est le sous-type) de tout type. C'est l'ensemble vide. Notez que si quelqu'un vous dit "la valeur de l'expression foo() appartient à l'ensemble vide" c'est très instructif - il vous dit que cette expression ne sera jamais complète son exécution normale; il va abandonner, de jeter ou de blocage. Il est l'exact opposé devoid
.Donc la suite n'a pas de sens (pseudo-C++, car
noreturn
n'est pas un premier de la classe C++ type)Mais la cession ci-dessous est tout à fait légitime, puisque
throw
est compris par le compilateur de ne pas revenir:Dans un monde parfait, vous pouvez utiliser
noreturn
que la valeur de retour de la fonctionraise()
ci-dessus:Malheureusement C++ ne le permet pas, probablement pour des raisons pratiques. Au lieu de cela il vous donne la possibilité d'utiliser
[[ noreturn ]]
attribut qui permet de guider les optimisations du compilateur et les avertissements.void
etvoid
jamais évalue àtrue
oufalse
ou quoi que ce soit d'autre.void
. etvoid
jamais évalue àtrue
depuisbool
est sous-type devoid
et non l'inverse. Encore une fois, tout ce qui est dit ici est de type théorique. rien C++-centrique.void
n'est pas un 1er de la classe des citoyens dans le C++ type de système.true
, je ne veux pas dire "la valeurtrue
du typebool
" mais la logique du sens, voir Curry-Howard correspondancey!=0 ? x/y : throw exception()
void(true)
est autorisé en C++. Il n'est pas.y != 0 ? x / y : throw exception()
n'a rien à voir avec la façon dontvoid
est traité comme un type.(void)true;
est parfaitement valide, comme la réponse suggère.void(true)
est quelque chose de complètement différent, du point de vue syntaxique. C'est une tentative de créer un nouvel objet de typevoid
par l'appel d'un constructeur avectrue
comme argument; cela échoue, entre autres raisons, parce quevoid
n'est pas de première classe.void
etnoreturn
dans le type de la théorie terme, en complément de l' (certes plus utile) des réponses pratiques ci-dessus.