Différence entre le spécificateur throw () C ++ 03 C ++ 11 noexcept

100

Y a-t-il une différence entre throw()et noexceptautre que la vérification au moment de l'exécution et de la compilation, respectivement?

Cet article Wikipedia C ++ 11 suggère que les spécificateurs de jet C ++ 03 sont obsolètes.
Pourquoi donc, est-il noexceptassez capable de couvrir tout cela au moment de la compilation?

[Remarque: j'ai vérifié cette question et cet article , mais je n'ai pas pu déterminer la raison solide de l'abandon.]

iammilind
la source
7
Selon cet bel article,noexcept des vérifications d'exécution peuvent également être nécessaires . La principale différence entre eux est que la rupture noexceptprovoque std::terminatetandis que la rupture throwprovoque std::unexpected. Également un comportement de déroulement de pile légèrement différent dans ces cas.
Fiktik
Il n'y a rien de "compile time" vérifié avec certaines spécifications d'exception qui sont vérifiées "runtime" sur d'autres. C'est juste un mythe créé par les opposants aux spécifications d'exception C ++.
curiousguy

Réponses:

129

Les spécificateurs d'exceptions étaient obsolètes car les spécificateurs d'exceptions sont généralement une mauvaise idée . noexcepta été ajouté parce que c'est la seule utilisation raisonnablement utile d'un spécificateur d'exception: savoir quand une fonction ne lèvera pas d'exception. Cela devient donc un choix binaire: des fonctions qui lanceront et des fonctions qui ne lanceront pas.

noexcepta été ajouté plutôt que de simplement supprimer tous les spécificateurs de jet autrement que throw()parce qu'il noexceptest plus puissant. noexceptpeut avoir un paramètre qui au moment de la compilation se résout en booléen. Si le booléen est vrai, alors les noexceptbâtons. Si le booléen est faux, alors noexceptne colle pas et la fonction peut lancer.

Ainsi, vous pouvez faire quelque chose comme ceci:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

CreateOtherClassLève- t-il des exceptions? Cela pourrait, si Tle constructeur par défaut de le peut. Comment le dire? Comme ça:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Ainsi, CreateOtherClass()lancera ssi le constructeur par défaut du type donné lance. Cela résout l'un des problèmes majeurs avec les spécificateurs d'exception: leur incapacité à propager la pile d'appels.

Vous ne pouvez pas faire ça avec throw().

Nicol Bolas
la source
+1 Réponse utile, pour moi en tout cas. Toujours à la recherche d'une réponse qui explique pourquoi je voudrais utiliser noexcept. Je n'ai jamais utilisé de throw()spécificateur, jamais et j'essaie de déterminer s'il noexceptfournit réellement un avantage (autre que la documentation vérifiée par le compilateur).
hmjd
Je viens de trouver ce stackoverflow.com/questions/10787766/… ...
hmjd
1
@NicolBolas est d'accord. mais si noexcept était une garantie, le compilateur pourrait vérifier si une fonction peut lancer ou non un destructeur. Ainsi être en mesure d'avertir un programmeur qu'une fonction est noexcept ou non.
Alex
2
@NicolBolas les appels d'exécution std::terminate. ce qui est bien pire ! le code peut se faufiler dans les versions qui ont des fonctions marquées noexcept et au moment de l'exécution (c'est-à-dire sur les sites des clients) les violations sont détectées. Je voulais dire que le compilateur garantit de générer du code qui ne lève pas d' exceptions en premier lieu.
Alex
2
@NicolBolas: Une autre différence à noter. Si une fonction est marquée, throws()alors si une exception est levée, la pile doit être déroulée jusqu'à la portée de cette fonction (de sorte que toutes les variables automatiques de la fonction sont détruites) à quel point terminate()est appelée (via unexpected()). Si une fonction est marquée, noexceptalors si une exception est levée, terminate est appelé (le déroulement de la pile est un détail défini par l'implémentation).
Martin York
33

noexcept n'est pas vérifié au moment de la compilation.

Une implémentation ne doit pas rejeter une expression simplement parce que lorsqu'elle est exécutée, elle lève ou pourrait lever une exception que la fonction contenant n'autorise pas.

Lorsqu'une fonction qui est déclarée noexceptou throw()tente de lever une exception, la seule différence est que l'on appelle terminateet que les autres appels unexpectedet ce dernier style de gestion des exceptions est effectivement obsolète.

CB Bailey
la source
Mais si une fonction virtuelle a throw()/ noexcept, la vérification du temps de compilation garantit qu'un remplacement a également.
curiousguy
2

std::unexpected() est appelée par le runtime C ++ lorsqu'une spécification d'exception dynamique est violée: une exception est levée à partir d'une fonction dont la spécification d'exception interdit les exceptions de ce type.

std::unexpected() peut également être appelé directement depuis le programme.

Dans les deux cas, std::unexpectedappelle le fichier actuellement installé std::unexpected_handler. Les std::unexpected_handlerappels par défaut std::terminate.

ma13
la source