noexcept-Spezifizierer (seit C++11)
Gibt an, ob eine Funktion Ausnahmen werfen kann.
Inhalt |
[bearbeiten] Syntax
noexcept
|
(1) | ||||||||
noexcept(Ausdruck) |
(2) | ||||||||
throw()
|
(3) | (veraltet in C++17) (in C++20 entfernt) | |||||||
noexcept(true)true ausgewertet wird, wird die Funktion so deklariert, dass sie keine Ausnahmen wirft. Eine ( nach noexcept ist immer Teil dieser Form (sie kann niemals einen Initialisierer einleiten).noexcept(true) (siehe dynamische Ausnahmespezifikation für ihre Semantik vor C++17)| expression | - | kontextuell konvertierbarer konstanter Ausdruck vom Typ bool
|
[bearbeiten] Erklärung
|
Die noexcept-Spezifikation ist kein Teil des Funktionstyps (genau wie die dynamische Ausnahmespezifikation) und kann nur als Teil eines Lambda-Deklarators oder eines obersten Funktionsdeklarators erscheinen, wenn Funktionen, Variablen, nicht-statische Datenelemente vom Typ Funktion, Zeiger auf Funktion, Referenz auf Funktion oder Zeiger auf Memberfunktion deklariert werden, sowie wenn ein Parameter oder Rückgabetyp in einer solchen Deklaration deklariert wird, der zufällig ein Zeiger oder eine Referenz auf eine Funktion ist. Sie kann nicht in einer typedef- oder Typalias-Deklaration erscheinen. void f() noexcept; // the function f() does not throw void (*fp)() noexcept(false); // fp points to a function that may throw void g(void pfa() noexcept); // g takes a pointer to function that doesn't throw // typedef int (*pf)() noexcept; // error |
(bis C++17) |
|
Die noexcept-Spezifikation ist Teil des Funktionstyps und kann als Teil eines beliebigen Funktionsdeklarators erscheinen. |
(seit C++17) |
Jede Funktion in C++ ist entweder *nicht werfend* oder *potenziell werfend*
- *potenziell werfende* Funktionen sind
|
(bis C++17) |
- Funktionen, die mit dem
noexcept-Spezifizierer deklariert wurden, dessen Ausdruck zufalseausgewertet wird - Funktionen, die ohne
noexcept-Spezifizierer deklariert wurden, außer
- Destruktoren, es sei denn, der Destruktor einer potenziell konstruierbaren Basisklasse oder eines Elements ist *potenziell werfend* (siehe unten)
- Standardkonstruktoren, Kopierkonstruktoren, Verschiebungskonstruktoren, die implizit deklariert oder bei ihrer ersten Deklaration standardmäßig gesetzt werden, es sei denn
- ein Konstruktor für eine Basisklasse oder ein Element, den die implizite Definition des Konstruktors aufrufen würde, ist *potenziell werfend* (siehe unten)
- ein Unterausdruck einer solchen Initialisierung, wie z. B. ein Standardargumentausdruck, ist *potenziell werfend* (siehe unten)
- ein Standard-Member-Initialisierer (nur für Standardkonstruktor) ist *potenziell werfend* (siehe unten)
- Kopierzuweisungsoperatoren, Verschiebungszuweisungsoperatoren, die implizit deklariert oder bei ihrer ersten Deklaration standardmäßig gesetzt werden, es sei denn, der Aufruf eines Zuweisungsoperators in der impliziten Definition ist *potenziell werfend* (siehe unten)
- Funktionen, die mit dem
|
(seit C++20) |
- nicht werfende Funktionen sind alle anderen (die mit einem noexcept-Spezifizierer, dessen Ausdruck zu
trueausgewertet wird, sowie Destruktoren, standardmäßig gesetzte spezielle Memberfunktionen und Deallokationsfunktionen)
Explizite Instanziierungen können den noexcept-Spezifizierer verwenden, aber das ist nicht zwingend erforderlich. Wenn verwendet, muss die Ausnahmespezifikation dieselbe sein wie für alle anderen Deklarationen. Eine Diagnose ist nur erforderlich, wenn die Ausnahmespezifikationen innerhalb einer einzelnen Übersetzungseinheit nicht identisch sind.
Funktionen, die sich nur in ihrer Ausnahmespezifikation unterscheiden, können nicht überladen werden (genau wie der Rückgabetyp ist die Ausnahmespezifikation Teil des Funktionstyps, aber nicht Teil der Funktionssignatur)(seit C++17).
void f() noexcept; void f(); // error: different exception specification void g() noexcept(false); void g(); // ok, both declarations for g are potentially-throwing
Zeiger (einschließlich Zeiger auf Memberfunktionen) auf nicht werfende Funktionen können zugewiesen oder zur Initialisierung verwendet werden(bis C++17)sind implizit konvertierbar in(seit C++17) Zeiger auf potenziell werfende Funktionen, aber nicht umgekehrt.
void ft(); // potentially-throwing void (*fn)() noexcept = ft; // error
Wenn eine virtuelle Funktion nicht werfend ist, müssen alle Deklarationen, einschließlich der Definition, aller Überschreiber ebenfalls nicht werfend sein, es sei denn, der Überschreiber ist als gelöscht definiert.
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // ill-formed: D::f is potentially-throwing, B::f is non-throwing void g() noexcept; // OK void h() = delete; // OK };
Nicht werfende Funktionen dürfen potenziell werfende Funktionen aufrufen. Wenn eine Ausnahme geworfen wird und die Suche nach einem Handler den äußersten Block einer nicht werfenden Funktion erreicht, wird std::terminate aufgerufen.
extern void f(); // potentially-throwing void g() noexcept { f(); // valid, even if f throws throw 42; // valid, effectively a call to std::terminate }
Die Ausnahmespezifikation einer Funktionsvorlagen-Spezialisierung wird nicht zusammen mit der Funktionsdeklaration instanziiert; sie wird erst instanziiert, wenn sie *benötigt wird* (wie unten definiert).
Die Ausnahmespezifikation einer implizit deklarierten speziellen Memberfunktion wird ebenfalls erst bei Bedarf ausgewertet (insbesondere erfordert die implizite Deklaration einer Memberfunktion einer abgeleiteten Klasse nicht, dass die Ausnahmespezifikation einer Basis-Memberfunktion instanziiert wird).
Wenn die noexcept-Spezifikation einer Funktionsvorlagen-Spezialisierung *benötigt wird*, aber noch nicht instanziiert wurde, werden die abhängigen Namen nachgeschlagen und alle in der Ausdruck verwendeten Vorlagen werden so instanziiert, als ob sie für die Deklaration der Spezialisierung wären.
Eine noexcept-Spezifikation 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 werden, erscheint aber in einem nicht ausgewerteten Operanden
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f unevaluated, but noexcept-spec is needed // error because instantiation of the noexcept specification // calculates sizeof(void) }
- die Spezifikation wird benötigt, um mit einer anderen Funktionsdeklaration verglichen zu werden (z. B. bei einem virtuellen Funktionsüberschreiber oder einer expliziten Spezialisierung einer Funktionsvorlage)
- in einer Funktionsdefinition
- die Spezifikation wird benötigt, weil eine standardmäßig gesetzte spezielle Memberfunktion sie überprüfen muss, um ihre eigene Ausnahmespezifikation zu bestimmen (dies geschieht nur, wenn die Spezifikation der standardmäßig gesetzten speziellen Memberfunktion selbst benötigt wird).
Formale Definition eines *potenziell werfenden Ausdrucks* (wird zur Bestimmung der standardmäßigen Ausnahmespezifikation von Destruktoren, Konstruktoren und Zuweisungsoperatoren wie oben beschrieben verwendet)
Ein Ausdruck e ist *potenziell werfend*, wenn
-
eein Funktionsaufruf an eine Funktion, einen Zeiger auf eine Funktion oder einen Zeiger auf eine Memberfunktion ist, die *potenziell werfend* ist , es sei denn,eist ein Kern-Konstanten-Ausdruck(bis C++17) -
eeinen impliziten Aufruf einer *potenziell werfenden* Funktion auslöst (wie z. B. ein überladener Operator, eine Allokationsfunktion in einemnew-Ausdruck, ein Konstruktor für ein Funktionsargument oder ein Destruktor, wenneein vollständiger Ausdruck ist) -
eeinthrow-Ausdruck ist -
eeindynamic_castist, der einen polymorphen Referenztyp konvertiert -
eeintypeid-Ausdruck ist, der auf einen dereferenzierten Zeiger auf einen polymorphen Typ angewendet wird -
eeinen sofortigen Unterausdruck hat, der potenziell werfend ist
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // implicit exception specification is noexcept(true) B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D() potentially-throwing because of the new operator // D::D(const D&) non-throwing // D::D(D&&) potentially-throwing: the default argument for B’s constructor may throw // D::~D() potentially-throwing // note; if A::~A() were virtual, this program would be ill-formed because an overrider // of a non-throwing virtual cannot be potentially-throwing };
[bearbeiten] Hinweise
Einer der Verwendungszwecke des konstanten Ausdrucks ist (zusammen mit dem noexcept-Operator) die Definition von Funktionsvorlagen, die für einige Typen noexcept deklarieren, für andere jedoch nicht.
Beachten Sie, dass eine noexcept-Spezifikation für eine Funktion keine Compile-Zeit-Prüfung ist; sie ist lediglich eine Methode für einen Programmierer, den Compiler darüber zu informieren, ob eine Funktion Ausnahmen werfen soll oder nicht. Der Compiler kann diese Informationen nutzen, um bestimmte Optimierungen für nicht werfende Funktionen zu ermöglichen und den noexcept-Operator zu aktivieren, der zur Compile-Zeit prüfen kann, ob ein bestimmter Ausdruck deklariert ist, Ausnahmen zu werfen. Container wie z. B. std::vector verschieben ihre Elemente, wenn der Verschiebungskonstruktor der Elemente noexcept ist, und kopieren andernfalls (es sei denn, der Kopierkonstruktor ist nicht zugänglich, aber ein potenziell werfender Verschiebungskonstruktor ist es, in welchem Fall die starke Ausnahmegarantie aufgehoben wird).
[bearbeiten] Veraltet
noexcept ist eine verbesserte Version von throw(), das in C++11 veraltet ist. Im Gegensatz zu throw() vor C++17 ruft noexcept nicht std::unexpected auf, kann den Stack entrollen oder auch nicht, und ruft std::terminate auf, was dem Compiler potenziell ermöglicht, noexcept ohne den Laufzeit-Overhead von throw() zu implementieren. Ab C++17 wird throw() als exaktes Äquivalent zu noexcept(true) neu definiert.
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_noexcept_function_type |
201510L |
(C++17) | Ausnahmespezifikationen als Teil des Typsystems |
[bearbeiten] Schlüsselwörter
noexcept, throw(seit C++17)(bis C++20)
[bearbeiten] Beispiel
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
[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 1330 | C++11 | Eine Ausnahmespezifikation kann eilig instanziiert werden | sie wird nur instanziiert, wenn sie benötigt wird |
| CWG 1740 | C++11 | Ein ( nach noexcept könnte einen Initialisierer starten |
es kann nur ein Teil von sein noexcept-Spezifikation |
| CWG 2039 | C++11 | Nur der Ausdruck vor der Konvertierung muss konstant sein | die Konvertierung muss ebenfalls sein gültig in einem konstanten Ausdruck |
[bearbeiten] Siehe auch
noexcept-Operator(C++11) |
stellt fest, ob ein Ausdruck Ausnahmen wirft |
| Dynamische Ausnahmespezifikation(bis C++17) | gibt an, welche Ausnahmen von einer Funktion geworfen werden (veraltet in C++11) |
throw-Ausdruck
|
signalisiert einen Fehler und überträgt die Kontrolle an den Fehlerbehandler |
| (C++11) |
konvertiert das Argument in ein xvalue, wenn der Move-Konstruktor nicht wirft (Funktionsvorlage) |