Dynamic exception specification (until C++17)
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) |
|||||||
| 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:
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) |
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:
e
ein Funktionsaufrufausdruck ist, sei
g
die Funktion, der Funktionszeiger oder der Zeiger auf eine Memberfunktion, der aufgerufen wird, dann
-
-
falls die Deklaration von
geine dynamische Ausnahmespezifikation verwendet, wird die Menge der potenziellen Ausnahmen vongzur Menge hinzugefügt;
-
falls die Deklaration von
|
(seit C++11) |
-
- andernfalls ist die Menge die Menge aller Typen.
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.
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).
e
ein
dynamic_cast
auf eine Referenz eines polymorphen Typs ist, besteht die Menge aus
std::bad_cast
.
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
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 |