noexcept
specifier
(since C++11)
Gibt an, ob eine Funktion Ausnahmen auslösen könnte.
Inhaltsverzeichnis |
Syntax
noexcept
|
(1) | ||||||||
noexcept(
expression
)
|
(2) | ||||||||
throw()
|
(3) |
(veraltet in C++17)
(entfernt in C++20) |
|||||||
noexcept(true)
(
nach
noexcept
ist immer Teil dieser Form (es kann niemals einen Initialisierer beginnen).
noexcept(true)
(siehe
dynamische Ausnahmespezifikation
für deren Semantik vor C++17)
| expression | - | kontextuell konvertierter konstanter Ausdruck vom Typ bool |
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 top-level Funktionsdeklarators erscheinen, wenn Funktionen, Variablen, nicht-statische Datenelemente vom Typ Funktion, Zeiger auf Funktion, Referenz auf Funktion oder Zeiger auf Elementfunktion deklariert werden, und auch wenn ein Parameter oder ein Rückgabetyp in einer dieser Deklarationen deklariert wird, der seinerseits ein Zeiger oder eine Referenz auf eine Funktion ist. Sie kann nicht in einer typedef - oder Typalias -Deklaration erscheinen. void f() noexcept; // die Funktion f() wirft keine Ausnahmen void (*fp)() noexcept(false); // fp zeigt auf eine Funktion, die Ausnahmen werfen kann void g(void pfa() noexcept); // g nimmt einen Zeiger auf eine Funktion, die keine Ausnahmen wirft // typedef int (*pf)() noexcept; // Fehler |
(bis C++17) |
|
Die noexcept-Spezifikation ist ein Teil des Funktionstyps und kann als Teil jedes Funktionsdeklarators erscheinen. |
(seit C++17) |
Jede Funktion in C++ ist entweder non-throwing oder potentially throwing :
- potentially-throwing Funktionen sind:
|
(bis C++17) |
-
-
Funktionen, die mit
noexcept-Spezifizierer deklariert sind, deren Ausdruck zufalseausgewertet wird -
Funktionen, die ohne
noexcept-Spezifizierer deklariert sind, außer für
-
- Destruktoren , es sei denn, der Destruktor einer potenziell konstruierten Basis oder Member ist potenziell werfend (siehe unten)
- Standardkonstruktoren , Kopierkonstruktoren , Move-Konstruktoren , die implizit deklariert oder bei ihrer ersten Deklaration standardmäßig sind, es sei denn
-
- ein Konstruktor für eine Basis oder Member, den die implizite Definition des Konstruktors aufrufen würde, ist potenziell werfend (siehe unten)
- ein Teilausdruck einer solchen Initialisierung, wie ein Standardargumentausdruck, ist potenziell werfend (siehe unten)
- ein Standard-Member-Initialisierer (nur für Standardkonstruktor) ist potenziell werfend (siehe unten)
- Kopierzuweisungs -Operatoren, Move-Zuweisungs -Operatoren, die implizit deklariert oder bei ihrer ersten Deklaration standardmäßig sind, es sei denn, der Aufruf eines Zuweisungsoperators in der impliziten Definition ist potenziell werfend (siehe unten)
-
Funktionen, die mit
|
(seit C++20) |
-
nicht-werfende Funktionen sind alle anderen (diejenigen mit noexcept-Spezifizierer, deren
expression
zu
trueausgewertet wird, sowie Destruktoren, defaulted special member functions und deallocation functions)
Explizite Instanziierungen können die noexcept-Spezifikation verwenden, sind dazu jedoch nicht verpflichtet. Falls verwendet, muss die Ausnahmespezifikation mit allen anderen Deklarationen übereinstimmen. 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(); // Fehler: Unterschiedliche Ausnahmespezifikation void g() noexcept(false); void g(); // Ok, beide Deklarationen für g sind potenziell werfend
Zeiger (einschließlich Zeiger auf Memberfunktionen) auf nicht-werfende Funktionen können zugewiesen werden oder zur Initialisierung verwendet werden (bis C++17) sind implizit konvertierbar zu (seit C++17) Zeigern auf potenziell werfende Funktionen, aber nicht umgekehrt.
void ft(); // potenziell werfend void (*fn)() noexcept = ft; // Fehler
Wenn eine virtuelle Funktion nicht werfend ist, müssen alle Deklarationen, einschließlich der Definition, jedes Überschreibers 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(); // ungültig: D::f ist potenziell werfend, B::f ist nicht-werfend void g() noexcept; // OK void h() = delete; // OK };
Nicht werfende Funktionen dürfen potenziell werfende Funktionen aufrufen. Immer wenn eine Ausnahme geworfen wird und die Suche nach einem Handler den äußersten Block einer nicht werfenden Funktion erreicht, wird die Funktion std::terminate aufgerufen:
extern void f(); // potenziell werfend void g() noexcept { f(); // gültig, selbst wenn f eine Exception wirft throw 42; // gültig, effektiv ein Aufruf von std::terminate }
Die Ausnahmespezifikation einer Funktionstemplate-Spezialisierung wird nicht zusammen mit der Funktionsdeklaration instanziiert; sie wird nur instanziiert, wenn benötigt (wie unten definiert).
Die 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 noexcept-Spezifikation einer Funktions-Templatespezialisierung benötigt wird, aber noch nicht instanziiert wurde, werden die abhängigen Namen gesucht und alle in dem expression verwendeten Templates so instanziiert, wie für die Deklaration der Spezialisierung.
Eine noexcept-Spezifikation 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 aber in einem nicht ausgewerteten Operanden
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f nicht ausgewertet, aber noexcept-Spezifikation wird benötigt // Fehler, weil die Instanziierung der noexcept-Spezifikation // 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).
Formale Definition eines potenziell werfenden Ausdrucks (verwendet zur Bestimmung der Standard-Ausnahmespezifikation von Destruktoren, Konstruktoren und Zuweisungsoperatoren wie oben beschrieben):
Ein Ausdruck
e
ist
potenziell werfend
, wenn:
-
eist ein Funktionsaufruf einer Funktion, eines Funktionszeigers oder eines Zeigers auf eine Elementfunktion, die potenziell werfend ist , es sei denn,eist ein Kernkonstantenausdruck (bis C++17) -
eführt einen impliziten Aufruf einer potenziell werfenden Funktion durch (wie einen überladenen Operator, eine Allokationsfunktion in einemnew-Ausdruck, einen Konstruktor für ein Funktionsargument oder einen Destruktor, fallseein vollständiger Ausdruck ist) -
eist einthrow-Ausdruck -
eist eindynamic_cast, der einen polymorphen Referenztyp umwandelt -
eist eintypeid-Ausdruck, angewendet auf einen dereferenzierten Zeiger auf einen polymorphen Typ -
ehat einen unmittelbaren Teilausdruck, 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; // implizite Ausnahmespezifikation ist 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() potenziell werfend wegen des new-Operators // D::D(const D&) nicht-werfend // D::D(D&&) potenziell werfend: der Standardparameter für B's Konstruktor könnte werfen // D::~D() potenziell werfend // Hinweis: wenn A::~A() virtuell wäre, wäre dieses Programm fehlerhaft, weil ein Überschreiber // eines nicht-werfenden virtuellen Destruktors nicht potenziell werfend sein kann };
Hinweise
Eine der Verwendungen des konstanten
Ausdrucks
ist (zusammen mit dem
noexcept
Operator
) die Definition von Funktions-Templates, die
noexcept
für einige Typen deklarieren, aber nicht für andere.
Beachten Sie, dass eine
noexcept
-Spezifikation bei einer Funktion keine Überprüfung zur Compile-Zeit ist; sie ist lediglich eine Methode für den Programmierer, den Compiler zu informieren, ob eine Funktion Ausnahmen werfen soll oder nicht. Der Compiler kann diese Information verwenden, um bestimmte Optimierungen bei nicht werfenden Funktionen zu ermöglichen sowie den
noexcept
-Operator
zu aktivieren, der zur Compile-Zeit überprüfen kann, ob ein bestimmter Ausdruck deklariert ist, Ausnahmen zu werfen. Zum Beispiel werden Container wie
std::vector
ihre Elemente verschieben, wenn der Move-Konstruktor der Elemente
noexcept
ist, und andernfalls kopieren (es sei denn, der Copy-Konstruktor ist nicht zugänglich, aber ein potenziell werfender Move-Konstruktor ist zugänglich, in welchem Fall die starke Ausnahmesicherheit aufgehoben wird).
Veraltet
noexcept
ist eine verbesserte Version von
throw
(
)
, die in C++11 als veraltet markiert wurde. Im Gegensatz zu pre-C++17
throw
(
)
ruft
noexcept
nicht
std::unexpected
auf, kann den Stack entfalten oder nicht, und ruft
std::terminate
auf, was dem Compiler potenziell erlaubt,
noexcept
ohne den Laufzeit-Overhead von
throw
(
)
zu implementieren. Seit C++17 wird
throw
(
)
als exaktes Äquivalent von
noexcept
(
true
)
neu definiert.
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_noexcept_function_type
|
201510L
|
(C++17) | Ausnahmespezifikationen Teil des Typsystems machen |
Schlüsselwörter
noexcept , throw (seit C++17) (bis C++20)
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 }
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 1330 | C++11 | eine Ausnahmespezifikation könnte eifrig instanziiert werden | sie wird nur bei Bedarf instanziiert |
| CWG 1740 | C++11 | eine ( nach noexcept könnte einen Initialisierer beginnen |
sie kann nur Teil einer
noexcept-Spezifikation sein |
| CWG 2039 | C++11 | nur der Ausdruck vor der Konvertierung muss konstant sein |
die Konvertierung muss ebenfalls
in einem konstanten Ausdruck gültig sein |
Siehe auch
noexcept
operator
(C++11)
|
bestimmt, ob ein Ausdruck Ausnahmen auslöst |
| Dynamische Ausnahmespezifikation (bis C++17) | spezifiziert, welche Ausnahmen von einer Funktion ausgelöst werden (veraltet in C++11) |
throw
expression
|
signalisiert einen Fehler und übergibt die Kontrolle an den Fehlerbehandler |
|
(C++11)
|
konvertiert das Argument zu einem xvalue, wenn der Move-Konstruktor keine Ausnahme auslöst
(Funktionstemplate) |