Namespaces
Variants

Dynamic exception specification (until C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
noexcept specification (C++11)
dynamic specification ( until C++17* )
noexcept operator (C++11)

Énumère les exceptions qu'une fonction peut lever directement ou indirectement.

Table des matières

Syntaxe

throw( type-id-list  (optionnel) ) (1) (obsolète en C++11)
(supprimé en C++17)
1) Spécification d'exception dynamique explicite.
type-id-list - liste séparée par des virgules de type-ids , un type-id représentant une pack expansion est suivi par des points de suspension (...) (depuis C++11)

Une spécification d'exception dynamique explicite ne doit apparaître que sur un déclarateur de fonction pour un type de fonction, un pointeur vers un type de fonction, une référence vers un type de fonction, ou un pointeur vers un type de fonction membre qui est le type de niveau supérieur d'une déclaration ou définition, ou sur un tel type apparaissant comme paramètre ou type de retour dans un déclarateur de fonction.

void f() throw(int);            // OK : déclaration de fonction
void (*pf)() throw (int);       // OK : déclaration de pointeur vers fonction
void g(void pfa() throw(int));  // OK : déclaration de paramètre pointeur vers fonction
typedef int (*pf)() throw(int); // Erreur : déclaration typedef

Explication

Si une fonction est déclarée avec un type T listé dans sa spécification d'exception dynamique, la fonction peut lever des exceptions de ce type ou d'un type qui en dérive.

Types incomplets , les pointeurs ou références vers des types incomplets autres que cv void* , et les types de références rvalue (depuis C++11) ne sont pas autorisés dans la spécification d'exception. Les types tableau et fonction, s'ils sont utilisés, sont ajustés vers les types pointeur correspondants, les qualifications cv de niveau supérieur sont également supprimées. les packs de paramètres sont autorisés (depuis C++11) .

Une spécification d'exception dynamique dont l'ensemble des types ajustés est vide (après expansion des packs) (depuis C++11) est non-lancante. Une fonction avec une spécification d'exception dynamique non-lancante n'autorise aucune exception.

Une spécification d'exception dynamique n'est pas considérée comme faisant partie du type d'une fonction.

Si la fonction lance une exception d'un type non listé dans sa spécification d'exception, la fonction std::unexpected est appelée. La fonction par défaut appelle std::terminate , mais elle peut être remplacée par une fonction fournie par l'utilisateur (via std::set_unexpected ) qui peut appeler std::terminate ou lancer une exception. Si l'exception lancée depuis std::unexpected est acceptée par la spécification d'exception, le déroulement de la pile se poursuit normalement. Si elle ne l'est pas, mais que std::bad_exception est autorisée par la spécification d'exception, std::bad_exception est lancée. Sinon, std::terminate est appelé.

Instanciation

La spécification d'exception dynamique d'une spécialisation de fonction template n'est pas instanciée avec la déclaration de fonction ; elle est instanciée uniquement quand nécessaire (tel que défini ci-dessous).

La spécification d'exception dynamique d'une fonction membre spéciale implicitement déclarée n'est é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 l'instanciation de la spécification d'exception d'une fonction membre de base).

Lorsque la spécification d'exception dynamique d'une spécialisation de fonction template est nécessaire , mais n'a pas encore été instanciée, les noms dépendants sont recherchés et tous les templates utilisés dans l' expression sont instanciés comme pour la déclaration de la spécialisation.

Une spécification d'exception dynamique 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() throw(std::array<char, sizeof(T)>);
int main()
{
    decltype(f<void>()) *p; // f non évaluée, mais la spécification d'exception est nécessaire
                            // erreur car l'instanciation de la spécification d'exception
                            // 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).

Exceptions potentielles

Chaque fonction f , pointeur vers fonction pf , et pointeur vers fonction membre pmf possède un ensemble d'exceptions potentielles , qui contient les types susceptibles d'être levés. L'ensemble de tous les types indique que n'importe quelle exception peut être levée. Cet ensemble est défini comme suit :

1) Si la déclaration de f , pf , ou pmf utilise une spécification d'exception dynamique qui n'autorise pas toutes les exceptions (jusqu'à C++11) , l'ensemble se compose des types listés dans cette spécification.
2) Sinon, si la déclaration de f , pf , ou pmf utilise noexcept(true) , l'ensemble est vide.
(depuis C++11)
3) Sinon, l'ensemble est l'ensemble de tous les types.

Note : pour les fonctions membres spéciales implicitement déclarées (constructeurs, opérateurs d'affectation et destructeurs) et pour les constructeurs d'héritage (depuis C++11) , l'ensemble des exceptions potentielles est une combinaison des ensembles des exceptions potentielles de tout ce qu'elles appelleraient : constructeurs/opérateurs d'affectation/destructeurs des membres de données non statiques non variants, bases directes, et, le cas échéant, bases virtuelles (incluant les expressions d'arguments par défaut, comme toujours).

Chaque expression e possède un ensemble d'exceptions potentielles . L'ensemble est vide si e est une expression constante de base , sinon, c'est l'union des ensembles d'exceptions potentielles de toutes les sous-expressions immédiates de e (incluant les expressions d'arguments par défaut ), combinée avec un autre ensemble qui dépend de la forme de e , comme suit :

1) Si e est une expression d'appel de fonction, soit g la fonction, le pointeur de fonction ou le pointeur vers une fonction membre qui est appelé, alors
  • si la déclaration de g utilise une spécification d'exception dynamique, l'ensemble des exceptions potentielles de g est ajouté à l'ensemble ;
(depuis C++11)
  • sinon, l'ensemble est l'ensemble de tous les types.
2) Si e appelle une fonction implicitement (c'est une expression d'opérateur et l'opérateur est surchargé, c'est une new-expression et la fonction d'allocation est surchargée, ou c'est une expression complète et le destructeur d'un temporaire est appelé), alors l'ensemble est l'ensemble de cette fonction.
3) Si e est une throw-expression , l'ensemble est l'exception qui serait initialisée par son opérande, ou l'ensemble de tous les types pour la throw-expression de re-lancement (sans opérande).
4) Si e est un dynamic_cast vers une référence d'un type polymorphe, l'ensemble se compose de std::bad_cast .
5) Si e est un typeid appliqué à un pointeur déréférencé vers un type polymorphe, l'ensemble se compose de std::bad_typeid .
6) Si e est une new-expression avec une taille de tableau non constante, et que la fonction d'allocation sélectionnée possède un ensemble non vide d'exceptions potentielles, l'ensemble contient std::bad_array_new_length .
(depuis C++11)
void f() throw(int); // l'ensemble de f() est "int"
void g();            // l'ensemble de g() est l'ensemble de tous les types
struct A { A(); };                  // l'ensemble de "new A" est l'ensemble de tous les types
struct B { B() noexcept; };         // l'ensemble de "B()" est vide
struct D() { D() throw (double); }; // l'ensemble de new D est l'ensemble de tous les types

Toutes les fonctions membres implicitement déclarées et les constructeurs hérités (depuis C++11) ont des spécifications d'exception, sélectionnées comme suit :

  • Si l'ensemble des exceptions potentielles est l'ensemble de tous les types, la spécification d'exception implicite autorise toutes les exceptions (la spécification d'exception est considérée comme présente, même si elle est inexprimable dans le code et se comporte comme s'il n'y avait pas de spécification d'exception) (jusqu'en C++11) est noexcept ( false ) (depuis C++11) .
  • Sinon, si l'ensemble des exceptions potentielles n'est pas vide, la spécification d'exception implicite énumère chaque type de l'ensemble.
  • Sinon, la spécification d'exception implicite est throw ( ) (jusqu'en C++11) noexcept ( true ) (depuis C++11) .
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};
struct B
{
    B() throw();
    B(const B&) = default; // la spécification d'exception est "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
    // Peut lever une exception d'un type qui correspondrait à un gestionnaire de type
    // std​::​bad_array_new_length, mais ne lève pas d'exception de mauvaise allocation
    (void*) new (std::nothrow) int[n];
    // D peut avoir les membres implicitement déclarés suivants :
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

Notes

Clang considère que la règle d'instanciation des spécifications d'exception dynamique est modifiée en C++11 par CWG1330 , voir LLVM #56349 .

Mots-clés

throw

Exemple

Note : il est préférable de compiler en mode C++98 pour éviter les avertissements. Incompatible avec C++17 et les révisions plus récentes.

#include <cstdlib>
#include <exception>
#include <iostream>
class X {};
class Y {};
class Z : public X {};
class W {};
void f() throw(X, Y) 
{
    bool n = false;
    if (n)
        throw X(); // OK, would call std::terminate()
    if (n)
        throw Z(); // also OK
    throw W(); // will call std::unexpected()
}
void handler()
{
    std::cerr << "That was unexpected!\n"; // flush needed
    std::abort();
}
int main()
{
    std::set_unexpected(handler);
    f();
}

Sortie :

That was unexpected!

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 25 C++98 le comportement de l'affectation et de l'initialisation
entre pointeurs vers membres avec différentes
spécifications d'exception n'était pas spécifié
appliquer la restriction
pour les pointeurs de fonction
et les références
CWG 973 C++98 la spécification d'exception pouvait contenir des types de fonctions, mais la
conversion correspondante des pointeurs de fonction n'était pas spécifiée
spécifiée
CWG 1330 C++98 une spécification d'exception pouvait être instanciée de manière anticipée elle n'est instanciée que si nécessaire
CWG 1267 C++11 les types de référence rvalue étaient autorisés dans les spécifications d'exception non autorisés
CWG 1351 C++98
C++11
l'argument par défaut (C++98) et l'initialiseur de membre par défaut
(C++11) étaient ignorés dans la spécification d'exception implicite
pris en compte
CWG 1777 C++11 throw ( T... ) n'était pas une spécification non-lancante
même si T est un pack vide
elle est non-lancante
si le pack est vide
CWG 2191 C++98 l'ensemble des exceptions potentielles d'une expression typeid
pouvait contenir bad_typeid même si elle ne peut pas être lancée
contient bad_typeid
seulement si elle peut être lancée

Voir aussi

noexcept specifier (C++11) spécifie si une fonction peut lever des exceptions