[dcl.attr.noreturn] fournit l'exemple suivant:
[[ noreturn ]] void f() {
throw "error";
// OK
}
mais je ne comprends pas à quoi ça sert [[noreturn]]
, car le type de retour de la fonction est déjà void
.
Alors, quel est l'intérêt de l' noreturn
attribut? Comment est-il censé être utilisé?
c++
c++11
attributes
noreturn
BЈовић
la source
la source
Réponses:
L'attribut noreturn est censé être utilisé pour les fonctions qui ne reviennent pas à l'appelant. Cela ne signifie pas des fonctions vides (qui retournent à l'appelant - elles ne renvoient tout simplement pas de valeur), mais des fonctions dans lesquelles le flux de contrôle ne reviendra pas à la fonction appelante une fois la fonction terminée (par exemple, les fonctions qui quittent l'application, boucle pour toujours ou lancer des exceptions comme dans votre exemple).
Cela peut être utilisé par les compilateurs pour effectuer des optimisations et générer de meilleurs avertissements. Par exemple, si
f
a l'attribut noreturn, le compilateur peut vous avertir d'g()
être du code mort lorsque vous écrivezf(); g();
. De même, le compilateur saura ne pas vous avertir des déclarations de retour manquantes après les appels àf()
.la source
execve
celle -là ne devrait pas revenir mais pourrait ? Doit-il avoir l' attribut noreturn ?noreturn
attribut.noreturn
ne peut être utilisé que si votre fonction est garantie de faire quelque chose qui termine le programme avant que le flux de contrôle puisse revenir à l'appelant - par exemple parce que vous appelez exit (), abort (), assert (0), etc.noreturn
. La gestion de cette exception n'est pas la même chose que son retour. Tout code dans letry
après l'appel est toujours inaccessible, et sinon,void
aucune affectation ou utilisation de la valeur de retour ne se produira.noreturn
n'indique pas au compilateur que la fonction ne renvoie aucune valeur. Il indique au compilateur que le flux de contrôle ne reviendra pas à l'appelant . Cela permet au compilateur de faire une variété d'optimisations - il n'a pas besoin de sauvegarder et de restaurer tout état volatil autour de l'appel, il peut éliminer le code mort tout code qui suivrait autrement l'appel, etc.la source
Cela signifie que la fonction ne se terminera pas. Le flux de contrôle n'atteindra jamais l'instruction après l'appel à
f()
:Les informations peuvent être utilisées par le compilateur / optimiseur de différentes manières. Le compilateur peut ajouter un avertissement indiquant que le code ci-dessus est inaccessible, et il peut modifier le code réel de
g()
de différentes manières, par exemple pour prendre en charge les continuations.la source
-Wno-return
et vous recevrez un avertissement. Ce n'est probablement pas celui que vous attendiez, mais c'est probablement suffisant pour vous dire que le compilateur a connaissance de ce que[[noreturn]]
c'est et qu'il peut en profiter. (Je suis un peu surpris que cela-Wunreachable-code
n'ait pas-Wmissing-noreturn
, l'avertissement implique que l'analyse de flux a déterminé que lestd::cout
n'est pas accessible. Je n'ai pas une nouvelle gcc assez à portée de main pour regarder l'ensemble généré, mais je ne serais pas surpris si l'appel àoperator<<
été abandonné-O1
déjà de supprimer ce code inaccessible sans[[noreturn]]
indice.[[noreturn]]
partir du code. Si cette unité de traduction n'avait qu'une déclaration de la fonction qui a été définie ailleurs, le compilateur ne pourrait pas supprimer ce code, car il ne sait pas que la fonction ne retourne pas. C'est là que l'attribut devrait aider le compilateur.Les réponses précédentes expliquaient correctement ce qu'est noreturn, mais pas pourquoi il existe. Je ne pense pas que les commentaires "optimisation" soient le but principal: les fonctions qui ne retournent pas sont rares et n'ont généralement pas besoin d'être optimisées. Je pense plutôt que la principale raison d'être de noreturn est d'éviter les avertissements faussement positifs. Par exemple, considérez ce code:
Si abort () n'avait pas été marqué "noreturn", le compilateur aurait pu avertir que ce code avait un chemin où f ne renvoie pas un entier comme prévu. Mais comme abort () est marqué sans retour, il sait que le code est correct.
la source
Type théoriquement parlant,
void
c'est ce qu'on appelle dans d'autres languesunit
outop
. Son équivalent logique est True . Toute valeur peut être convertie légitimement envoid
(chaque type est un sous-type devoid
). Pensez-y comme un ensemble «univers»; il n'y a pas d'opérations communes à toutes les valeurs du monde, il n'y a donc pas d'opérations valides sur une valeur de typevoid
. En d'autres termes, vous dire que quelque chose appartient à l'univers ne vous donne aucune information - vous le savez déjà. Donc, ce qui suit est sain:Mais la mission ci-dessous n'est pas:
[[noreturn]]
, D'autre part, que l' on appelle parfoisempty
,Nothing
,Bottom
ouBot
et est l'équivalent logique de faux . Il n'a aucune valeur du tout et une expression de ce type peut être transtypée en (c'est-à-dire est un sous-type de) n'importe quel type. Ceci est l'ensemble vide. Notez que si quelqu'un vous dit que «la valeur de l'expression foo () appartient à l'ensemble vide», c'est très informatif - cela vous dit que cette expression ne terminera jamais son exécution normale; il va abandonner, lancer ou se bloquer. C'est exactement le contraire devoid
.Donc, ce qui suit n'a pas de sens (pseudo-C ++, car ce
noreturn
n'est pas un type C ++ de première classe)Mais l'affectation ci-dessous est parfaitement légitime, car il
throw
est entendu par le compilateur de ne pas retourner:Dans un monde parfait, vous pouvez utiliser
noreturn
comme valeur de retour pour la fonctionraise()
ci-dessus:Malheureusement, C ++ ne le permet pas, probablement pour des raisons pratiques. Au lieu de cela, il vous permet d'utiliser un
[[ noreturn ]]
attribut qui aide à guider les optimisations et les avertissements du compilateur.la source
void
etvoid
n'évalue àtrue
oufalse
ou quoi que ce soit d' autre.true
, je ne veux pas dire "la valeurtrue
du typebool
" mais le sens logique, voir la correspondance Curry-Howard(void)true;
est parfaitement valable, comme le suggère la réponse.void(true)
est quelque chose de complètement différent, syntaxiquement. Il s'agit d'une tentative de créer un nouvel objet de typevoid
en appelant un constructeur avectrue
comme argument; cela échoue, entre autres raisons, car cevoid
n'est pas de première classe.