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)

Listet die Ausnahmen auf, die eine Funktion direkt oder indirekt werfen kann.

Inhaltsverzeichnis

Syntax

throw( type-id-list  (optional) ) (1) (in C++11 veraltet)
(in C++17 entfernt)
1) Explizite dynamische Exception-Spezifikation.
type-id-list - durch Kommas getrennte Liste von type-ids , ein type-id, das eine pack expansion repräsentiert, wird von einer Auslassungspunkten (...) gefolgt (seit C++11)

Eine explizite dynamische Exception-Spezifikation darf nur auf einem Funktionsdeklarator für einen Funktionstyp, Zeiger auf Funktionstyp, Referenz auf Funktionstyp oder Zeiger auf Member-Funktionstyp erscheinen, der der oberste Typ einer Deklaration oder Definition ist, oder auf einem solchen Typ, der als Parameter- oder Rückgabetyp in einem Funktionsdeklarator erscheint.

void f() throw(int);            // OK: Funktionsdeklaration
void (*pf)() throw (int);       // OK: Zeiger auf Funktionsdeklaration
void g(void pfa() throw(int));  // OK: Zeiger auf Funktionsparameterdeklaration
typedef int (*pf)() throw(int); // Fehler: Typdeklaration

Erklärung

Wenn eine Funktion mit einem Typ T in ihrer dynamischen Ausnahmespezifikation deklariert wird, darf die Funktion Ausnahmen dieses Typs oder eines davon abgeleiteten Typs werfen.

Unvollständige Typen , Zeiger oder Referenzen auf unvollständige Typen außer cv void* , und Rvalue-Referenztypen (seit C++11) sind in der Ausnahmespezifikation nicht erlaubt. Array- und Funktionstypen werden, falls verwendet, auf entsprechende Zeigertypen angepasst, ebenso werden oberste cv-Qualifizierungen entfernt. Parameterpacks sind erlaubt (seit C++11) .

Eine dynamische Ausnahmespezifikation, deren Menge angepasster Typen leer ist (nachdem alle Packs erweitert wurden) (seit C++11) ist nicht-werfend. Eine Funktion mit einer nicht-werfenden dynamischen Ausnahmespezifikation erlaubt keine Ausnahmen.

Eine dynamische Ausnahmespezifikation wird nicht als Teil des Funktionstyps betrachtet.

Wenn die Funktion eine Ausnahme eines Typs wirft, der nicht in ihrer Ausnahmespezifikation aufgeführt ist, wird die Funktion std::unexpected aufgerufen. Die Standardfunktion ruft std::terminate auf, kann jedoch durch eine benutzerdefinierte Funktion ersetzt werden (über std::set_unexpected ), die std::terminate aufrufen oder eine Ausnahme werfen kann. Wenn die von std::unexpected geworfene Ausnahme durch die Ausnahmespezifikation akzeptiert wird, setzt sich das Stack-Unwinding wie gewohnt fort. Wenn nicht, aber std::bad_exception durch die Ausnahmespezifikation erlaubt ist, wird std::bad_exception geworfen. Andernfalls wird std::terminate aufgerufen.

Instanziierung

Die dynamische Ausnahmespezifikation einer Funktions-Template-Spezialisierung wird nicht zusammen mit der Funktionsdeklaration instanziiert; sie wird nur dann instanziiert, wenn sie benötigt wird (wie unten definiert).

Die dynamische Ausnahmespezifikation eines implizit deklarierten speziellen Memberfunktion wird ebenfalls nur bei Bedarf ausgewertet (insbesondere erfordert die implizite Deklaration einer Memberfunktion einer abgeleiteten Klasse nicht, dass die Ausnahmespezifikation einer Basis-Memberfunktion instanziiert wird).

Wenn die dynamische Ausnahmespezifikation einer Funktions-Template-Spezialisierung benötigt wird, aber noch nicht instanziiert wurde, werden die abhängigen Namen nachgeschlagen und alle in dem Ausdruck verwendeten Templates so instanziiert, wie für die Deklaration der Spezialisierung.

Eine dynamische Ausnahmespezifikation einer Funktion wird in den folgenden Kontexten als benötigt betrachtet:

  • in einem Ausdruck, in dem die Funktion durch Überladungsauflösung ausgewählt wird
  • die Funktion ist odr-used
  • die Funktion wäre odr-used, erscheint jedoch in einem nicht ausgewerteten Operanden
template<class T>
T f() throw(std::array<char, sizeof(T)>);
int main()
{
    decltype(f<void>()) *p; // f wird nicht ausgewertet, aber die Ausnahmespezifikation wird benötigt
                            // Fehler, weil die Instanziierung der Ausnahmespezifikation
                            // sizeof(void) berechnet
}
  • Die Spezifikation wird benötigt, um sie mit einer anderen Funktionsdeklaration zu vergleichen (z.B. bei einem Überschreiben einer virtuellen Funktion oder bei einer expliziten Spezialisierung eines Funktions-Templates)
  • in einer Funktionsdefinition
  • Die Spezifikation wird benötigt, weil eine defaulted special member function diese überprüfen muss, um ihre eigene Exception-Spezifikation zu bestimmen (dies erfolgt nur, wenn die Spezifikation der defaulted special member function selbst benötigt wird).

Mögliche Ausnahmen

Jede Funktion f , jeder Zeiger auf Funktion pf und jeder Zeiger auf Member-Funktion pmf hat einen Satz potenzieller Ausnahmen , der aus Typen besteht, die geworfen werden könnten. Der Satz aller Typen zeigt an, dass jede Ausnahme geworfen werden kann. Dieser Satz ist wie folgt definiert:

1) Wenn die Deklaration von f , pf , oder pmf eine dynamische Ausnahmespezifikation verwendet die nicht alle Ausnahmen erlaubt (bis C++11) , besteht die Menge aus den in dieser Spezifikation aufgeführten Typen.
2) Andernfalls, wenn die Deklaration von f , pf , oder pmf noexcept(true) verwendet, ist die Menge leer.
(seit C++11)
3) Andernfalls ist die Menge die Menge aller Typen.

Hinweis: Für implizit deklarierte spezielle Elementfunktionen (Konstruktoren, Zuweisungsoperatoren und Destruktoren) und für vererbende Konstruktoren (seit C++11) ist der Satz möglicher Ausnahmen eine Kombination der Sätze möglicher Ausnahmen aller Elemente, die sie aufrufen würden: Konstruktoren/Zuweisungsoperatoren/Destruktoren von nicht-varianten nicht-statischen Datenelementen, direkten Basisklassen und, falls zutreffend, virtuellen Basisklassen (einschließlich Standardargumentausdrücke, wie immer).

Jeder Ausdruck e hat eine Menge potentieller Ausnahmen . Die Menge ist leer, falls e ein Core Constant Expression ist, andernfalls ist sie die Vereinigung der Mengen potentieller Ausnahmen aller unmittelbaren Teilausdrücke von e (einschließlich Default Argument Expressions ), kombiniert mit einer weiteren Menge, die von der Form von e abhängt, wie folgt:

1) Wenn e ein Funktionsaufrufausdruck ist, sei g die Funktion, der Funktionszeiger oder der Zeiger auf eine Memberfunktion, der aufgerufen wird, dann
  • falls die Deklaration von g eine dynamische Ausnahmespezifikation verwendet, wird die Menge der potenziellen Ausnahmen von g zur Menge hinzugefügt;
(seit C++11)
  • andernfalls ist die Menge die Menge aller Typen.
2) Wenn e eine Funktion implizit aufruft (es ist ein Operatorausdruck und der Operator ist überladen, es ist ein new-expression und die Allokationsfunktion ist überladen, oder es ist ein vollständiger Ausdruck und der Destruktor eines temporären Objekts wird aufgerufen), dann ist die Menge die Menge dieser Funktion.
3) Wenn e ein throw-expression ist, dann ist die Menge die Ausnahme, die durch ihren Operanden initialisiert werden würde, oder die Menge aller Typen für den erneuten Auswurf (ohne Operand).
4) Wenn e ein dynamic_cast auf eine Referenz eines polymorphen Typs ist, besteht die Menge aus std::bad_cast .
5) Wenn e ein typeid auf einen dereferenzierten Zeiger auf einen polymorphen Typ angewendet wird, besteht die Menge aus std::bad_typeid .
6) Wenn e ein new-expression mit einer nicht-konstanten Array-Größe ist und die ausgewählte Allokationsfunktion eine nicht-leere Menge potenzieller Ausnahmen hat, besteht die Menge aus std::bad_array_new_length .
(since C++11)
void f() throw(int); // f()s Menge ist "int"
void g();            // g()s Menge ist die Menge aller Typen
struct A { A(); };                  // "new A"s Menge ist die Menge aller Typen
struct B { B() noexcept; };         // "B()"s Menge ist leer
struct D() { D() throw (double); }; // new Ds Menge ist die Menge aller Typen

Alle implizit deklarierten Memberfunktionen und vererbte Konstruktoren (seit C++11) haben Ausnahmespezifikationen, die wie folgt ausgewählt werden:

  • Wenn die Menge der potenziellen Ausnahmen die Menge aller Typen ist, erlaubt die implizite Ausnahmespezifikation alle Ausnahmen (die Ausnahmespezifikation wird als vorhanden betrachtet, auch wenn sie im Code nicht ausdrückbar ist und sich verhält, als gäbe es keine Ausnahmespezifikation) (bis C++11) ist noexcept ( false ) (seit C++11) .
  • Andernfalls, wenn die Menge der potenziellen Ausnahmen nicht leer ist, listet die implizite Ausnahmespezifikation jeden Typ aus der Menge auf.
  • Andernfalls ist die implizite Ausnahmespezifikation throw ( ) (bis C++11) noexcept ( true ) (seit 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; // Ausnahmespezifikation ist "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
    // Kann eine Ausnahme eines Typs werfen, der zu einem Handler vom Typ
    // std​::​bad_array_new_length passen würde, wirft aber keine bad allocation Ausnahme
    (void*) new (std::nothrow) int[n];
    // D kann die folgenden implizit deklarierten Member haben:
    // 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);
};

Hinweise

Clang betrachtet, dass die Regel zur Instanziierung der dynamischen Exception-Spezifikation in C++11 durch CWG1330 geändert wurde, siehe LLVM #56349 .

Schlüsselwörter

throw

Beispiel

Hinweis: Am besten im C++98-Modus kompilieren, um Warnungen zu vermeiden. Nicht kompatibel mit C++17 und neueren Revisionen.

#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();
}

Ausgabe:

That was unexpected!

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 25 C++98 das Verhalten von Zuweisung und Initialisierung
zwischen Zeigern auf Member mit unterschiedlichen
Ausnahmespezifikationen war nicht spezifiziert
die Einschränkung für Funktionszeiger
und Referenzen anwenden
CWG 973 C++98 Ausnahmespezifikation kann Funktionstypen enthalten, aber die
entsprechende Funktionszeigerkonvertierung war nicht spezifiziert
spezifiziert
CWG 1330 C++98 eine Ausnahmespezifikation könnte eifrig instanziiert werden wird nur bei Bedarf instanziiert
CWG 1267 C++11 Rvalue-Referenztypen waren in Ausnahmespezifikationen erlaubt nicht erlaubt
CWG 1351 C++98
C++11
Standardargument (C++98) und Standard-Member-Initialisierer
(C++11) wurden in impliziter Ausnahmespezifikation ignoriert
werden berücksichtigt
CWG 1777 C++11 throw ( T... ) war keine nicht-werfende
Spezifikation selbst wenn T ein leeres Paket ist
ist nicht-werfend
wenn das Paket leer ist
CWG 2191 C++98 die Menge potentieller Ausnahmen eines typeid Ausdrucks
könnte bad_typeid enthalten selbst wenn es nicht geworfen werden kann
enthält bad_typeid
nur wenn es geworfen werden kann

Siehe auch

noexcept Specifier (C++11) gibt an, ob eine Funktion Ausnahmen auslösen könnte