Quel est le but de noreturn?

[dcl.attr.noreturn] fournit l’exemple suivant:

[[ noreturn ]] void f() { throw "error"; // OK } 

mais je ne comprends pas quel est le but de [[noreturn]] , car le type de retour de la fonction est déjà void .

Alors, quel est le but de l’atsortingbut noreturn ? Comment est-il censé être utilisé?

    L’atsortingbut noreturn est censé être utilisé pour les fonctions qui ne sont pas renvoyées à l’appelant. Cela ne veut pas dire des fonctions vides (qui retournent à l’appelant – elles ne renvoient simplement pas de valeur), mais des fonctions où le stream de contrôle ne retournera pas à la fonction appelante après la fin de la fonction (par exemple, des fonctions qui quittent l’application, boucle pour toujours ou jeter des exceptions comme dans votre exemple).

    Cela peut être utilisé par les compilateurs pour effectuer certaines optimisations et générer de meilleurs avertissements. Par exemple, si f possède l’atsortingbut noreturn, le compilateur pourrait vous avertir que g() est un code mort lorsque vous écrivez f(); g(); f(); g(); . De même, le compilateur ne saura pas vous avertir des instructions de retour manquantes après les appels à f() .

    noreturn ne dit pas au compilateur que la fonction ne renvoie aucune valeur. Il indique au compilateur que le stream de contrôle ne sera pas renvoyé à l’appelant . Cela permet au compilateur d’effectuer diverses optimisations – il n’a pas besoin de sauvegarder et de restaurer l’état volatil autour de l’appel, il peut éliminer tout code qui suivrait l’appel, etc.

    Cela signifie que la fonction ne sera pas terminée. Le stream de contrôle ne basha jamais l’instruction après l’appel à f() :

     void g() { f(); // unreachable: std::cout << "No! That's impossible" << std::endl; } 

    Les informations peuvent être utilisées par le compilateur / optimiseur de différentes manières. Le compilateur peut append un avertissement indiquant que le code ci-dessus est inaccessible, et il peut modifier le code actuel de g() de différentes manières, par exemple pour prendre en charge les continuations.

    Tapez théoriquement, le void est ce que l’on appelle dans d’autres langues unit ou top . Son équivalent logique est True . Toute valeur peut être légitimement void (chaque type est un sous-type de void ). Pensez-y comme “univers” ensemble; 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 type void . En d’autres termes, vous dire que quelque chose appartient à l’ensemble de l’univers ne vous donne aucune information – vous le savez déjà. Donc, ce qui suit est le son:

     (void)5; (void)foo(); // whatever foo() does 

    Mais la tâche ci-dessous n’est pas:

     void raise(); void f(int y) { int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be? cout << x << endl; } 

    [[noreturn]] , d'autre part, s'appelle parfois empty , Nothing , Bottom ou Bot et est l'équivalent logique de False . Il n'a aucune valeur et une expression de ce type peut être convertie en un type (c'est-à-dire est un sous-type). C'est le jeu vide. Notez que si quelqu'un vous dit "la valeur de l'expression foo () appartient à l'ensemble vide", elle est très informative - elle vous indique que cette expression ne terminera jamais son exécution normale; ça va avorter, lancer ou pendre. C'est l'exact opposé du void .

    Donc, ce qui suit n'a pas de sens (pseudo-C ++, puisque noreturn n'est pas un type C ++ de première classe)

     void foo(); (noreturn)5; // obviously a lie; the expression 5 does "return" (noreturn)foo(); // foo() returns void, and therefore returns 

    Mais la tâche ci-dessous est parfaitement légitime, car le compilateur considère que le renvoi ne revient pas:

     void f(int y) { int x = y!=0 ? 100/y : throw exception(); cout << x << endl; } 

    Dans un monde parfait, vous pouvez utiliser noreturn comme valeur de retour pour la fonction raise() ci-dessus:

     noreturn raise() { throw exception(); } ... int x = y!=0 ? 100/y : raise(); 

    Malheureusement, C ++ ne le permet pas, probablement pour des raisons pratiques. Au lieu de cela, vous avez la possibilité d'utiliser l'atsortingbut [[ noreturn ]] qui aide à guider les optimisations et les avertissements du compilateur.

    Les réponses précédentes expliquaient correctement ce que noreturn est, mais pas pourquoi il existe. Je ne pense pas que les commentaires “d’optimisation” constituent l’objective principal: les fonctions qui ne sont pas renvoyées 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 alertes faussement positives. Par exemple, considérons ce code:

     int f(bool b){ if (b) { return 7; } else { abort(); } } 

    Si abort () n’avait pas été marqué “noreturn”, le compilateur aurait pu avertir que ce code avait un chemin où f ne retournait pas un nombre entier comme prévu. Mais comme abort () est marqué pas de retour, il sait que le code est correct.