noexcept
specifier
(since C++11)
Spécifie si une fonction peut lever des exceptions.
Table des matières |
Syntaxe
noexcept
|
(1) | ||||||||
noexcept(
expression
)
|
(2) | ||||||||
throw()
|
(3) |
(obsolète en C++17)
(supprimé en C++20) |
|||||||
noexcept(true)
(
suivant
noexcept
fait toujours partie de cette forme (elle ne peut jamais commencer un initialiseur).
noexcept(true)
(voir
spécification d'exception dynamique
pour sa sémantique avant C++17)
| expression | - | expression constante contextuellement convertie de type bool |
Explication
|
La spécification noexcept ne fait pas partie du type de fonction (tout comme la spécification d'exception dynamique ) et ne peut apparaître que comme partie d'un déclarateur lambda ou d'un déclarateur de fonction de niveau supérieur lors de la déclaration de fonctions, de variables, de membres de données non statiques de type fonction, de pointeurs vers des fonctions, de références vers des fonctions, ou de pointeurs vers des fonctions membres, et également lors de la déclaration d'un paramètre ou d'un type de retour dans l'une de ces déclarations qui s'avère être un pointeur ou une référence vers une fonction. Elle ne peut pas apparaître dans une déclaration de typedef ou alias de type . void f() noexcept; // la fonction f() ne lance pas void (*fp)() noexcept(false); // fp pointe vers une fonction qui peut lancer void g(void pfa() noexcept); // g prend un pointeur vers une fonction qui ne lance pas // typedef int (*pf)() noexcept; // erreur |
(jusqu'en C++17) |
|
La spécification noexcept fait partie du type de fonction et peut apparaître comme partie de tout déclarateur de fonction . |
(depuis C++17) |
Toute fonction en C++ est soit non-throwing soit potentially throwing :
- potentially-throwing fonctions sont :
|
(jusqu'à C++17) |
-
-
fonctions déclarées avec le spécificateur
noexceptdont l' expression évalue àfalse -
fonctions déclarées sans le spécificateur
noexceptsauf pour
-
- destructeurs sauf si le destructeur de toute base ou membre potentiellement construit est potentiellement lanceur (voir ci-dessous)
- constructeurs par défaut , constructeurs de copie , constructeurs de déplacement implicitement déclarés ou définis par défaut lors de leur première déclaration sauf si
-
- un constructeur pour une base ou membre que la définition implicite du constructeur appellerait est potentiellement lanceur (voir ci-dessous)
- une sous-expression d'une telle initialisation, telle qu'une expression d'argument par défaut, est potentiellement lanceur (voir ci-dessous)
- un initialiseur de membre par défaut (pour le constructeur par défaut uniquement) est potentiellement lanceur (voir ci-dessous)
- opérateurs d'affectation par copie , opérateurs d'affectation par déplacement implicitement déclarés ou définis par défaut lors de leur première déclaration sauf si l'invocation de tout opérateur d'affectation dans la définition implicite est potentiellement lanceur (voir ci-dessous)
-
fonctions déclarées avec le spécificateur
|
(depuis C++20) |
-
les fonctions non levantes sont toutes les autres (celles avec le spécificateur noexcept dont
l'expression
évalue à
trueainsi que les destructeurs, les fonctions membres spéciales par défaut et les fonctions de désallocation)
Instanciations explicites peuvent utiliser le spécificateur noexcept, mais ce n'est pas obligatoire. S'il est utilisé, la spécification d'exception doit être la même que pour toutes les autres déclarations. Un diagnostic n'est requis que si les spécifications d'exception ne sont pas identiques au sein d'une même unité de traduction.
Les fonctions qui ne diffèrent que par leur spécification d'exception ne peuvent pas être surchargées (tout comme le type de retour, la spécification d'exception fait partie du type de fonction, mais ne fait pas partie de la signature de fonction) (depuis C++17) .
void f() noexcept; void f(); // erreur : spécification d'exception différente void g() noexcept(false); void g(); // ok, les deux déclarations pour g sont potentiellement levantes
Les pointeurs (y compris les pointeurs vers des fonctions membres) vers des fonctions non levantes d'exception peuvent être assignés ou utilisés pour initialiser (jusqu'à C++17) sont implicitement convertibles en (depuis C++17) des pointeurs vers des fonctions potentiellement levantes d'exception, mais pas l'inverse.
void ft(); // potentiellement levante void (*fn)() noexcept = ft; // erreur
Si une fonction virtuelle est non-lancée, toutes les déclarations, y compris la définition, de chaque surcharge doivent également être non-lancées, à moins que la surcharge ne soit définie comme supprimée :
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // incorrect : D::f est potentiellement levée d'exception, B::f est non levée void g() noexcept; // OK void h() = delete; // OK };
Les fonctions non levantes sont autorisées à appeler des fonctions potentiellement levantes. Chaque fois qu'une exception est levée et que la recherche d'un gestionnaire rencontre le bloc le plus externe d'une fonction non levante, la fonction std::terminate est appelée :
extern void f(); // potentiellement levante d'exceptions void g() noexcept { f(); // valide, même si f lève une exception throw 42; // valide, équivaut effectivement à un appel à std::terminate }
La spécification d'exception d'une spécialisation de fonction template n'est pas instanciée avec la déclaration de la fonction ; elle est instanciée uniquement quand nécessaire (tel que défini ci-dessous).
La spécification d'exception d'une fonction membre spéciale implicitement déclarée n'est également évaluée que lorsque nécessaire (en particulier, la déclaration implicite d'une fonction membre d'une classe dérivée ne nécessite pas que la spécification d'exception d'une fonction membre de base soit instanciée).
Lorsque la spécification noexcept d'une spécialisation de fonction modèle est nécessaire , mais n'a pas encore été instanciée, les noms dépendants sont recherchés et tous les modèles utilisés dans l' expression sont instanciés comme pour la déclaration de la spécialisation.
Une spécification noexcept d'une fonction est considérée comme nécessaire dans les contextes suivants :
- dans une expression, où la fonction est sélectionnée par résolution de surcharge
- la fonction est odr-used
- la fonction serait odr-used mais apparaît dans un opérande non évalué
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f non évaluée, mais la spécification noexcept est nécessaire // erreur car l'instanciation de la spécification noexcept // calcule sizeof(void) }
- la spécification est nécessaire pour comparer à une autre déclaration de fonction (par exemple, sur un surchargeur de fonction virtuelle ou sur une spécialisation explicite d'un modèle de fonction)
- dans une définition de fonction
- la spécification est nécessaire car une fonction membre spéciale par défaut doit la vérifier afin de décider de sa propre spécification d'exception (cela se produit uniquement lorsque la spécification de la fonction membre spéciale par défaut est, elle-même, nécessaire).
Définition formelle de potentially-throwing expression (utilisée pour déterminer la spécification d'exception par défaut des destructeurs, constructeurs et opérateurs d'assignation comme décrit ci-dessus) :
Une expression
e
est
potentiellement-lancée
si :
-
eest un appel de fonction à une fonction, un pointeur de fonction, ou un pointeur de fonction membre qui est potentiellement levante , sauf sieest une expression constante de base (jusqu'en C++17) -
eeffectue un appel implicite à une fonction potentiellement levante (telle qu'un opérateur surchargé, une fonction d'allocation dans unenew-expression, un constructeur pour un argument de fonction, ou un destructeur sieest une expression complète) -
eest unethrow-expression -
eest undynamic_castqui convertit un type référence polymorphe -
eest une expressiontypeidappliquée à un pointeur déréférencé vers un type polymorphe -
ea une sous-expression immédiate qui est potentiellement levante
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // la spécification d'exception implicite est noexcept(true) B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D() potentiellement levante à cause de l'opérateur new // D::D(const D&) non levante // D::D(D&&) potentiellement levante : l'argument par défaut du constructeur de B peut lever // D::~D() potentiellement levante // note : si A::~A() était virtuel, ce programme serait mal formé car un surchargeur // d'une fonction virtuelle non levante ne peut pas être potentiellement levante };
Notes
L'un des usages de l'expression
constante
est (avec l'
noexcept
opérateur
) de définir des modèles de fonction qui déclarent
noexcept
pour certains types mais pas pour d'autres.
Notez qu'une spécification
noexcept
sur une fonction n'est pas une vérification à la compilation ; c'est simplement une méthode permettant au programmeur d'informer le compilateur si une fonction doit lever des exceptions ou non. Le compilateur peut utiliser cette information pour activer certaines optimisations sur les fonctions non levantes ainsi que pour activer l'
noexcept
opérateur
, qui peut vérifier à la compilation si une expression particulière est déclarée comme pouvant lever des exceptions. Par exemple, les conteneurs tels que
std::vector
déplaceront leurs éléments si le constructeur de déplacement des éléments est
noexcept
, et les copieront sinon (sauf si le constructeur de copie n'est pas accessible, mais qu'un constructeur de déplacement potentiellement levant l'est, auquel cas la garantie d'exception forte est abandonnée).
Obsolète
noexcept
est une version améliorée de
throw
(
)
, qui est dépréciée en C++11. Contrairement à
throw
(
)
pré-C++17,
noexcept
n'appellera pas
std::unexpected
, pourra ou non dérouler la pile, et appellera
std::terminate
, ce qui permet potentiellement au compilateur d'implémenter
noexcept
sans la surcharge d'exécution de
throw
(
)
. Depuis C++17,
throw
(
)
est redéfini comme étant exactement équivalent à
noexcept
(
true
)
.
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_noexcept_function_type
|
201510L
|
(C++17) | Rendre les spécifications d'exception partie du système de type |
Mots-clés
noexcept , throw (depuis C++17) (jusqu'à C++20)
Exemple
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 1330 | C++11 | une spécification d'exception pouvait être instanciée de manière anticipée | elle n'est instanciée que si nécessaire |
| CWG 1740 | C++11 | un ( suivant noexcept pouvait démarrer un initialiseur |
il ne peut faire partie que
d'une spécification noexcept |
| CWG 2039 | C++11 | seule l'expression avant conversion devait être constante |
la conversion doit également être
valide dans une expression constante |
Voir aussi
noexcept
opérateur
(C++11)
|
détermine si une expression lève des exceptions |
| Spécification d'exception dynamique (jusqu'à C++17) | spécifie les exceptions levées par une fonction (obsolète en C++11) |
throw
expression
|
signale une erreur et transfère le contrôle au gestionnaire d'erreurs |
|
(C++11)
|
convertit l'argument en xvalue si le constructeur de déplacement ne lève pas d'exception
(modèle de fonction) |