Handling exceptions
Une exception peut être gérée par un gestionnaire.
Table des matières |
Gestionnaire
catch
(
attr
(optionnel)
type-specifier-seq
declarator
)
compound-statement
|
(1) | ||||||||
catch
(
attr
(optionnel)
type-specifier-seq
abstract-declarator
(optionnel)
)
compound-statement
|
(2) | ||||||||
catch
(
...
)
compound-statement
|
(3) | ||||||||
| attr | - | (since C++11) n'importe quel nombre d' attributs , s'applique au paramètre |
| type-specifier-seq | - | partie d'une déclaration de paramètre formel, identique à celle d'une liste de paramètres de fonction |
| declarator | - | partie d'une déclaration de paramètre, identique à celle d'une liste de paramètres de fonction |
| abstract-declarator | - | partie d'une déclaration de paramètre sans nom, identique à celle d'une liste de paramètres de fonction |
| compound-statement | - | une instruction composée |
La déclaration de paramètre dans un gestionnaire décrit le(s) type(s) d'exceptions qui peuvent provoquer l'entrée dans ce gestionnaire.
Si le paramètre est déclaré avoir l'un des types suivants, le programme est mal formé :
|
(depuis C++11) |
- un pointeur vers un type incomplet autre que (éventuellement qualifié cv) void
- une référence lvalue vers un type incomplet
Si le paramètre est déclaré de type « tableau de
T
» ou de type fonction
T
, le type est ajusté en « pointeur vers
T
».
Un gestionnaire avec un type de paramètre
T
peut être abrégé en « gestionnaire de type
T
».
Correspondance des exceptions
Chaque try bloc est associé à plusieurs gestionnaires, ces gestionnaires forment une séquence de gestionnaires. Lorsqu'une exception est levée depuis un try bloc, les gestionnaires de la séquence sont essayés dans l'ordre d'apparition pour correspondre à l'exception.
Un gestionnaire correspond à un
objet exception
de type
E
si l'une des conditions suivantes est satisfaite :
- Le gestionnaire est de type « T éventuellement qualifié cv » ou « référence à lvalue vers T éventuellement qualifié cv », et l'une des conditions suivantes est satisfaite :
-
-
EetTsont du même type (en ignorant les qualificateurs cv de niveau supérieur). -
Test une classe de base publique non ambiguë deE.
-
-
Le gestionnaire est de type « T éventuellement qualifié cv »
Tou const T & oùTest un type pointeur ou pointeur-sur-membre, et l'une des conditions suivantes est satisfaite :
-
-
Eest un type pointeur ou pointeur-vers-membre qui peut être converti enTpar au moins l'une des conversions suivantes :
-
- Une conversion de pointeur standard n'impliquant pas de conversions vers des pointeurs vers des classes privées, protégées ou ambiguës.
-
| (depuis C++17) |
|
(depuis C++11) |
Le gestionnaire catch ( ... ) intercepte les exceptions de tout type. S'il est présent, il ne peut être que le dernier gestionnaire dans une séquence de gestionnaires. Ce gestionnaire peut être utilisé pour garantir qu'aucune exception non interceptée ne puisse s'échapper d'une fonction qui offre une garantie d'absence d'exception .
try { f(); } catch (const std::overflow_error& e) {} // cette clause s'exécute si f() lance std::overflow_error (règle du même type) catch (const std::runtime_error& e) {} // cette clause s'exécute si f() lance std::underflow_error (règle de la classe de base) catch (const std::exception& e) {} // cette clause s'exécute si f() lance std::logic_error (règle de la classe de base) catch (...) {} // cette clause s'exécute si f() lance std::string ou int ou tout autre type non apparenté
Si aucune correspondance n'est trouvée parmi les gestionnaires d'un bloc try , la recherche d'un gestionnaire correspondant se poursuit dans un bloc try englobant dynamiquement du même thread (since C++11) .
Si aucun gestionnaire correspondant n'est trouvé, std::terminate est invoqué ; que la pile soit ou non unwound avant cette invocation de std::terminate est défini par l'implémentation.
Gestion des exceptions
Lorsqu'une exception est levée, le contrôle est transféré vers le gestionnaire le plus proche avec un type correspondant ; « le plus proche » signifie le gestionnaire pour lequel l'instruction composée ou la liste d'initialisation des membres (si présente) suivant le mot-clé try a été le plus récemment entré par le fil d'exécution et n'a pas encore été quitté.
Initialisation du paramètre handler
Le paramètre déclaré dans la liste des paramètres (le cas échéant), de type « T éventuellement qualifié cv » ou « référence à lvalue vers T éventuellement qualifié cv », est initialisé à partir de l'
objet exception
, de type
E
, comme suit :
-
Si
Test une classe de base deE, le paramètre est copy-initialized à partir d'une lvalue de typeTdésignant le sous-objet de classe de base correspondant de l'objet exception. -
Sinon, le paramètre est copy-initialized à partir d'une lvalue de type
Edésignant l'objet exception.
La durée de vie du paramètre se termine lorsque le gestionnaire se termine, après la destruction de tout objet ayant une durée de stockage automatique initialisé dans le gestionnaire.
Lorsque le paramètre est déclaré comme un objet, toute modification de cet objet n'affectera pas l'objet d'exception.
Lorsque le paramètre est déclaré comme une référence à un objet, toute modification de l'objet référencé constitue une modification de l'objet d'exception et aura un effet si cet objet est relancé.
Activation du gestionnaire
Un gestionnaire est considéré comme actif lorsque l'initialisation est terminée pour le paramètre (le cas échéant) du gestionnaire.
De plus, un gestionnaire implicite est considéré comme actif lorsque std::terminate est appelé suite à un lancer d'exception.
Un gestionnaire n'est plus considéré comme actif lorsque le gestionnaire se termine.
L'exception avec le gestionnaire le plus récemment activé qui est toujours actif est appelée l'exception actuellement gérée . Une telle exception peut être relancée .
Flux de contrôle
La compound-statement d'un gestionnaire est une instruction à flux de contrôle limité :
void f() { goto label; // erreur try { goto label; // erreur } catch (...) { goto label: // OK label: ; } }
Notes
Stack unwinding se produit pendant que le contrôle est transféré à un gestionnaire. Lorsqu'un gestionnaire devient actif, le déroulement de pile est déjà terminé.
L'exception levée par l'expression throw throw 0 ne correspond pas à un gestionnaire de type pointeur ou pointeur-vers-membre.
|
(depuis C++11) |
Objets d'exception ne peuvent jamais avoir de types tableau ou fonction, par conséquent un gestionnaire de référence à un type tableau ou fonction ne correspond jamais à aucun objet d'exception.
Il est possible d'écrire des gestionnaires qui ne pourront jamais être exécutés, par exemple en plaçant un gestionnaire pour une classe dérivée finale après un gestionnaire pour une classe de base publique non ambiguë correspondante :
try { f(); } catch (const std::exception& e) {} // sera exécuté si f() lance std::runtime_error catch (const std::runtime_error& e) {} // code mort !
De nombreuses implémentations étendent excessivement la résolution de CWG issue 388 aux gestionnaires de référence vers des types pointeur non-const :
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // devrait passer, mais échoue sur MSVC et Clang }
Mots-clés
Exemple
L'exemple suivant démontre plusieurs cas d'utilisation des gestionnaires :
#include <iostream> #include <vector> int main() { try { std::cout << "Throwing an integer exception...\n"; throw 42; } catch (int i) { std::cout << " the integer exception was caught, with value: " << i << '\n'; } try { std::cout << "Creating a vector of size 5... \n"; std::vector<int> v(5); std::cout << "Accessing the 11th element of the vector...\n"; std::cout << v.at(10); // vector::at() throws std::out_of_range } catch (const std::exception& e) // caught by reference to base { std::cout << " a standard exception was caught, with message: '" << e.what() << "'\n"; } }
Sortie possible :
Throwing an integer exception... the integer exception was caught, with value: 42 Creating a vector of size 5... Accessing the 11th element of the vector... a standard exception was caught, with message: 'out_of_range'
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 tel que publié | Comportement correct |
|---|---|---|---|
| CWG 98 | C++98 | une instruction switch peut transférer le contrôle vers un gestionnaire | interdit |
| CWG 210 | C++98 | les expressions throw étaient comparées aux gestionnaires |
les objets d'exception sont
comparés aux gestionnaires |
| CWG 388 | C++98 |
une exception de type pointeur ou pointeur vers membre ne
pouvait pas être comparée par une référence const vers un type différent |
rendue comparable
lorsqu'elle est convertible |
| CWG 1166 | C++98 |
le comportement n'était pas spécifié lorsqu'un gestionnaire dont
le type est une référence vers un type de classe abstraite est comparé |
les types de classes abstraites ne sont
pas autorisés pour les gestionnaires |
| CWG 1769 | C++98 |
lorsque le type du gestionnaire est une base du type de
l'objet d'exception, un constructeur de conversion pourrait être utilisé pour l'initialisation du paramètre du gestionnaire |
le paramètre est initialisé par copie
à partir de la sous-classe de base correspondante du sous-objet de l'objet d'exception |
| CWG 2093 | C++98 |
un objet d'exception de type pointeur vers objet ne pouvait pas correspondre à un
gestionnaire de type pointeur vers objet via une conversion de qualification |
autorisé |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 14.4 Gestion d'une exception [except.handle]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 14.4 Gestion d'une exception [except.handle]
- Norme C++17 (ISO/CEI 14882:2017) :
-
- 18.3 Gestion d'une exception [except.handle]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 15.3 Gestion d'une exception [except.handle]
- Norme C++11 (ISO/IEC 14882:2011) :
-
- 15.3 Gestion d'une exception [except.handle]
- Norme C++03 (ISO/CEI 14882:2003) :
-
- 15.3 Gestion d'une exception [except.handle]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 15.3 Gestion d'une exception [except.handle]