Namespaces
Variants

noexcept specifier (since C++11)

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)

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)
1) Gleichbedeutend mit noexcept(true)
2) Falls expression zu true ausgewertet wird, wird die Funktion als nicht auslösend deklariert. Eine ( nach noexcept ist immer Teil dieser Form (es kann niemals einen Initialisierer beginnen).
3) Gleichbedeutend mit 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 zu false ausgewertet wird
  • Funktionen, die ohne noexcept -Spezifizierer deklariert sind, außer für
  • 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)
  • Vergleichsoperatoren , die bei ihrer ersten Deklaration standardmäßig festgelegt sind, es sei denn, der Aufruf eines Vergleichsoperators in der impliziten Definition ist potenziell auslösend (siehe unten)
(seit C++20)
  • nicht-werfende Funktionen sind alle anderen (diejenigen mit noexcept-Spezifizierer, deren expression zu true ausgewertet 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:

  • e ist ein Funktionsaufruf einer Funktion, eines Funktionszeigers oder eines Zeigers auf eine Elementfunktion, die potenziell werfend ist , es sei denn, e ist ein Kernkonstantenausdruck (bis C++17)
  • e führt einen impliziten Aufruf einer potenziell werfenden Funktion durch (wie einen überladenen Operator, eine Allokationsfunktion in einem new -Ausdruck, einen Konstruktor für ein Funktionsargument oder einen Destruktor, falls e ein vollständiger Ausdruck ist)
  • e ist ein throw -Ausdruck
  • e ist ein dynamic_cast , der einen polymorphen Referenztyp umwandelt
  • e ist ein typeid -Ausdruck, angewendet auf einen dereferenzierten Zeiger auf einen polymorphen Typ
  • e hat 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
konvertiert das Argument zu einem xvalue, wenn der Move-Konstruktor keine Ausnahme auslöst
(Funktionstemplate)