[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 point de l'attribut noreturn
? Comment est-il censé être utilisé?
L'attribut noreturn est censé être utilisé pour les fonctions qui ne retournent pas à l'appelant. Cela ne signifie pas que les fonctions void (qui retournent à l'appelant - elles ne renvoient pas une valeur), mais des fonctions pour lesquelles le flux de contrôle ne retournera pas à la fonction appelante une fois la fonction terminée (par exemple, les fonctions qui quittent l'application, boucle pour toujours ou jette des exceptions comme dans votre exemple).
Ceci peut être utilisé par les compilateurs pour effectuer certaines optimisations et générer de meilleurs avertissements. Par exemple, si f
a l'attribut noreturn, le compilateur peut vous avertir que g()
est un code mort lorsque vous écrivez f(); g();
. De même, le compilateur saura ne pas vous avertir des instructions de retour manquantes après les appels à f()
.
noreturn
n'indique pas au compilateur que la fonction ne renvoie aucune valeur. Il indique au compilateur que le flux de contrôle ne retournera pas à l'appelant. Cela permet au compilateur de faire diverses optimisations - il n’est pas nécessaire de sauvegarder et de restaurer un état volatile autour de l’appel, il peut éliminer par code mort tout code qui suivrait l’appel, etc.
Cela signifie que la fonction ne sera pas terminée. Le flux de contrôle ne touchera 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 ajouter un avertissement indiquant que le code ci-dessus est inaccessible et modifier le code actuel de g()
de différentes manières, par exemple pour prendre en charge les continuations.
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'objectif principal: les fonctions qui ne reviennent 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érons ce code:
int f(bool b){
if (b) {
return 7;
} else {
abort();
}
}
Si avort () 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 parce que abort () est marqué pas de retour, il sait que le code est correct.
Tapez théoriquement, void
est ce qui est appelé dans d'autres langages unit
ou top
. Son équivalent logique est True. Toute valeur peut être légitimement convertie en void
(chaque type est un sous-type de void
). Pensez-y comme un "univers" défini; il n'y a pas d'opération commune à all les valeurs du monde, il n'y a donc pas d'opération valide sur une valeur de type void
. En d'autres termes, vous dire que quelque chose appartient à l'univers ne vous donne aucune information - vous le savez déjà. Donc, voici le son:
(void)5;
(void)foo(17); // whatever foo(17) does
Mais la mission 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]]
, Quant à lui, est parfois appelé empty
, Nothing
, Bottom
ou Bot
et constitue l'équivalent logique de - False. Il n’a aucune valeur et une expression de ce type peut être convertie en (c’est-à-dire 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", elle est fortement informative - elle vous indique que cette expression ne terminera jamais son exécution normale; il va avorter, jeter ou accrocher. C'est l'exact opposé de 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 comprend que throw
ne renvoie 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, il vous donne la possibilité d'utiliser l'attribut [[ noreturn ]]
, Qui aide à guider les optimisations et les avertissements du compilateur.