Quel est le point de noreturn?

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

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

Mais je ne comprends pas à quoi sert [[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é?

144
demandé sur Deduplicator 2012-05-10 20:22:25

5 réponses

L'attribut noreturn est censé être utilisé pour les fonctions qui ne reviennent pas à l'appelant. Cela ne signifie pas que les fonctions void (qui retournent à l'appelant-elles ne renvoient pas de valeur), mais les fonctions où le flux de contrôle ne reviendra pas à la fonction appelante après la fin de la fonction (par exemple, les fonctions qui quittent l'application, boucle pour toujours ou jettent 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 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().

169
répondu sepp2k 2012-05-10 16:42:42

noreturn ne dit pas au compilateur que la fonction ne renvoie aucune valeur. Il indique au compilateur que control flow 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 un État volatile autour de l'appel, il peut éliminer tout code qui suivrait l'appel, etc.

54
répondu Stephen Canon 2012-05-10 16:25:51

Cela signifie que la fonction ne sera pas terminée. Le flux de contrôle n'atteindra jamais l'instruction suivant l'appel de 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 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 supporter les continuations.

23
répondu David Rodríguez - dribeas 2012-05-10 16:25:22

Type théoriquement parlant, void est ce qui est appelé dans d'autres langues unit ou top. Son équivalent logique est True. Toute valeur peut être légitimement exprimées à void (chaque type est un sous-type de void). Pensez-y comme un ensemble "univers"; il n'y a pas d'opérations communes à toutes les valeurs dans le monde, donc il n'y a pas d'opérations valides sur une valeur de type void. Autrement dit, vous dire que quelque chose appartient à l'ensemble de l'univers ne vous donne aucune information que ce soit, vous le savez déjà. Donc, ce qui suit est son:

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

Mais l'affectation 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, on 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 (c'est-à-dire sous-type de) n'importe quel 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 hautement informatif - il vous indique que cette expression ne terminera jamais son exécution normale; elle avortera, lancera ou se bloquera. 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 L'affectation ci-dessous est parfaitement légitime, puisque throw est compris par le compilateur pour ne pas retourner:

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 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 [[ noreturn ]] attribut qui aide à guider les optimisations et les avertissements du compilateur.

9
répondu Elazar 2017-09-01 14:50:37

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 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érez ce code:

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

Si abort () n'avait pas été marqué "noreturn" , le compilateur aurait peut-être averti que ce code avait un chemin où f ne retourne un entier comme prévu. Mais comme abort () est marqué no return, il sait que le code est correct.

7
répondu nyh 2017-11-22 22:23:54