Dynamische Ausnahmespezifikation (bis C++17)
Listet die Ausnahmen auf, die eine Funktion direkt oder indirekt auslösen kann.
Inhalt |
[bearbeiten] Syntax
throw(type-id-list (optional)) |
(1) | (in C++11 veraltet) (removed in C++17) | |||||||
| type-id-list | - | durch Kommas getrennte Liste von Type-IDs, ein Type-ID, das eine Pack-Erweiterung darstellt, wird von einer Ellipse (...) gefolgt(seit C++11) |
Eine explizite dynamische Ausnahmespezifikation darf nur auf einem Funktionsdeklarator für einen Funktionaltyp, einen Zeiger auf einen Funktionaltyp, eine Referenz auf einen Funktionaltyp oder einen Zeiger auf eine Memberfunktionstyp 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: function declaration void (*pf)() throw (int); // OK: pointer to function declaration void g(void pfa() throw(int)); // OK: pointer to function parameter declaration typedef int (*pf)() throw(int); // Error: typedef declaration
[bearbeiten] Erklärung
Wenn eine Funktion mit einem Typ T deklariert wird, der in ihrer dynamischen Ausnahmespezifikation aufgeführt ist, kann die Funktion Ausnahmen dieses Typs oder eines davon abgeleiteten Typs auslösen.
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 Funktionaltypen werden, falls sie verwendet werden, in entsprechende Zeigertypen umgewandelt; oberste cv-Qualifizierungen werden ebenfalls verworfen. Parameter-Packs sind erlaubt(seit C++11).
Eine dynamische Ausnahmespezifikation, deren Satz 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 gilt nicht als Teil des Typs einer Funktion.
Wenn die Funktion eine Ausnahme des Typs auslöst, der nicht in ihrer Ausnahmespezifikation aufgeführt ist, wird die Funktion std::unexpected aufgerufen. Die Standardfunktion ruft std::terminate auf, kann aber durch eine vom Benutzer bereitgestellte Funktion (über std::set_unexpected) ersetzt werden, die std::terminate aufrufen oder eine Ausnahme auslösen kann. Wenn die von std::unexpected ausgelöste Ausnahme von der Ausnahmespezifikation akzeptiert wird, wird die Stapelentwindung wie gewohnt fortgesetzt. Wenn nicht, aber std::bad_exception von der Ausnahmespezifikation zugelassen wird, wird std::bad_exception ausgelöst. Andernfalls wird std::terminate aufgerufen.
[bearbeiten] Instanziierung
Die dynamische Ausnahmespezifikation einer Funktionsschablonenspezialisierung wird nicht zusammen mit der Funktionsdeklaration instanziiert; sie wird nur instanziiert, wenn sie *benötigt* wird (wie unten definiert).
Die dynamische Ausnahmespezifikation einer 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 Funktionsschablonenspezialisierung *benötigt* wird, aber noch nicht instanziiert wurde, werden die abhängigen Namen gesucht und alle in einem Ausdruck verwendeten Schablonen so instanziiert, als ob es sich um die Deklaration der Spezialisierung handelt.
Eine dynamische Ausnahmespezifikation einer Funktion gilt in den folgenden Kontexten als *benötigt*:
- in einem Ausdruck, wo die Funktion durch Überladungsauflösung ausgewählt wird
- die Funktion wird ODR-verwendet
- die Funktion würde ODR-verwendet, erscheint aber in einem nicht ausgewerteten Operand
template<class T> T f() throw(std::array<char, sizeof(T)>); int main() { decltype(f<void>()) *p; // f unevaluated, but exception specification is needed // error because instantiation of the exception specification // calculates sizeof(void) }
- die Spezifikation wird benötigt, um sie mit einer anderen Funktionsdeklaration zu vergleichen (z. B. bei einem virtuellen Funktionsüberschreiber oder einer expliziten Spezialisierung einer Funktionsschablone)
- in einer Funktionsdefinition
- die Spezifikation wird benötigt, weil eine standardmäßig definierte spezielle Memberfunktion sie überprüfen muss, um ihre eigene Ausnahmespezifikation zu bestimmen (dies geschieht nur, wenn die Spezifikation der standardmäßig definierten speziellen Memberfunktion selbst benötigt wird).
[bearbeiten] Mögliche Ausnahmen
Jede Funktion f, jeder Zeiger auf eine Funktion pf und jeder Zeiger auf eine Memberfunktion pmf hat ein Set möglicher Ausnahmen, das aus Typen besteht, die ausgelöst werden können. Ein Set aller Typen bedeutet, dass jede Ausnahme ausgelöst werden kann. Dieses Set ist wie folgt definiert:
f, pf oder pmf eine dynamische Ausnahmespezifikation verwendet, die nicht alle Ausnahmen zulässt(bis C++11), besteht das Set aus den in dieser Spezifikation aufgeführten Typen.| (seit C++11) |
Hinweis: Für implizit deklarierte spezielle Memberfunktionen (Konstruktoren, Zuweisungsoperatoren und Destruktoren) und für die erbenden Konstruktoren(seit C++11) ist das Set möglicher Ausnahmen eine Kombination aus den Sets der möglichen Ausnahmen von allem, was sie aufrufen: Konstruktoren/Zuweisungsoperatoren/Destruktoren von nicht-varianten nicht-statischen Datenmitgliedern, direkten Basen und, wo angebracht, virtuellen Basen (einschließlich Standardargumentausdrücken, wie immer).
Jeder Ausdruck e hat ein Set möglicher Ausnahmen. Das Set ist leer, wenn e ein Kernkonstantausdruck ist, andernfalls ist es die Vereinigung der Sets möglicher Ausnahmen aller unmittelbaren Unterausdrücke von e (einschließlich Standardargumentausdrücken), kombiniert mit einem weiteren Set, das von der Form von e abhängt, wie folgt:
e ein Funktionsaufrufausdruck ist, sei g die aufgerufene Funktion, der Funktionszeiger oder der Zeiger auf eine Memberfunktion, dann:- wenn die Deklaration von
geine dynamische Ausnahmespezifikation verwendet, wird das Set möglicher Ausnahmen vongzum Set hinzugefügt;
- wenn die Deklaration von
|
(seit C++11) |
- andernfalls ist das Set das Set aller Typen.
e eine Funktion implizit aufruft (es ist ein Operator-Ausdruck und der Operator ist überladen, es ist ein new-Ausdruck und die Allokationsfunktion ist überladen, oder es ist ein vollständiger Ausdruck und der Destruktor eines Temporären wird aufgerufen), dann ist das Set das Set dieser Funktion.e ein throw-Ausdruck ist, ist das Set die Ausnahme, die durch seinen Operanden initialisiert würde, oder das Set aller Typen für den erneut werfenden throw-Ausdruck (ohne Operand).e ein dynamic_cast zu einer Referenz auf einen polymorphen Typ ist, besteht das Set aus std::bad_cast.e ein typeid ist, das auf einen dereferenzierten Zeiger auf einen polymorphen Typ angewendet wird, besteht das Set aus std::bad_typeid.|
6) Wenn e ein new-Ausdruck mit einer nicht-konstanten Array-Größe ist und die ausgewählte Allokationsfunktion ein nicht-leeres Set möglicher Ausnahmen hat, besteht das Set aus std::bad_array_new_length. |
(seit C++11) |
void f() throw(int); // f()'s set is "int" void g(); // g()'s set is the set of all types struct A { A(); }; // "new A"'s set is the set of all types struct B { B() noexcept; }; // "B()"'s set is empty struct D() { D() throw (double); }; // new D's set is the set of all types
Alle implizit deklarierten Memberfunktionen und erbenden Konstruktoren(seit C++11) haben Ausnahmespezifikationen, die wie folgt ausgewählt werden:
- Wenn das Set möglicher Ausnahmen das Set aller Typen ist, erlaubt die implizite Ausnahmespezifikation alle Ausnahmen (die Ausnahmespezifikation gilt als vorhanden, obwohl sie im Code nicht ausgedrückt werden kann und sich so verhält, als ob keine Ausnahmespezifikation vorhanden wäre)ist noexcept(false)(bis C++11)ist noexcept(false)(seit C++11).
- Andernfalls, wenn das Set möglicher Ausnahmen nicht leer ist, listet die implizite Ausnahmespezifikation jeden Typ aus dem Set 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; // exception specification is "noexcept(true)" B(B&&, int = (throw Y(), 0)) noexcept; ~B() throw(Y); }; int n = 7; struct D : public A, public B { // May throw an exception of a type that would match a handler of type // std::bad_array_new_length, but does not throw a bad allocation exception (void*) new (std::nothrow) int[n]; // D may have the following implicitly-declared members: // 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); };
[bearbeiten] Hinweise
Clang betrachtet die Regel der Instanziierung von dynamischen Ausnahmespezifikationen in C++11 durch CWG1330 geändert, siehe LLVM #56349.
[bearbeiten] Schlüsselwörter
[bearbeiten] Beispiel
Hinweis: Am besten in C++98-Modus kompilieren, um Warnungen zu vermeiden. Inkompatibel 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!
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 25 | C++98 | Das Verhalten von Zuweisung und Initialisierung zwischen Zeigern auf Member mit unterschiedlichen Ausnahmespezifikationen war undefiniert |
Beschränkung anwenden für Funktionszeiger und Referenzen |
| CWG 973 | C++98 | Ausnahmespezifikation kann Funktionstypen enthalten, aber die entsprechende Funktionszeigerkonvertierung wurde nicht spezifiziert |
spezifiziert |
| CWG 1330 | C++98 | eine Ausnahmespezifikation könnte eifrig instanziiert werden | sie wird nur instanziiert, wenn sie benötigt wird |
| 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 der impliziten Ausnahmespezifikation ignoriert |
wurden berücksichtigt |
| CWG 1777 | C++11 | throw(T...) war keine nicht-werfende Spezifikation, auch wenn T ein leeres Pack ist |
sie ist nicht-werfend wenn das Pack leer ist |
| CWG 2191 | C++98 | das Set möglicher Ausnahmen eines typeid-Ausdruckskönnte bad_typeid enthalten, auch wenn es nicht ausgelöst werden kann |
enthält bad_typeidnur, wenn es ausgelöst werden kann |
[bearbeiten] Siehe auch
noexcept Spezifikator(C++11) |
spezifiziert, ob eine Funktion Ausnahmen werfen kann |