Namensräume
Varianten
Aktionen

noexcept-Spezifizierer (seit C++11)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
Ausnahmen
try Block
Ausnahmen auslösen
Ausnahmen behandeln
Ausnahmespezifikation
    noexcept-Spezifikation (C++11)
    dynamische Spezifikation (bis C++17*)
noexcept Operator (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)
1) Identisch mit noexcept(true)
2) Wenn der Ausdruck zu 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).
3) Identisch mit 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 zu false ausgewertet wird
  • Funktionen, die ohne noexcept-Spezifizierer deklariert wurden, außer
  • 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)
  • Vergleichsoperatoren, die bei ihrer ersten Deklaration standardmäßig gesetzt werden, es sei denn, der Aufruf eines Vergleichsoperators in der impliziten Definition ist *potenziell werfend* (siehe unten)
(seit C++20)
  • nicht werfende Funktionen sind alle anderen (die mit einem noexcept-Spezifizierer, dessen Ausdruck zu true ausgewertet 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

  • e ein Funktionsaufruf an eine Funktion, einen Zeiger auf eine Funktion oder einen Zeiger auf eine Memberfunktion ist, die *potenziell werfend* ist , es sei denn, e ist ein Kern-Konstanten-Ausdruck(bis C++17)
  • e einen impliziten Aufruf einer *potenziell werfenden* Funktion auslöst (wie z. B. ein überladener Operator, eine Allokationsfunktion in einem new-Ausdruck, ein Konstruktor für ein Funktionsargument oder ein Destruktor, wenn e ein vollständiger Ausdruck ist)
  • e ein throw-Ausdruck ist
  • e ein dynamic_cast ist, der einen polymorphen Referenztyp konvertiert
  • e ein typeid-Ausdruck ist, der auf einen dereferenzierten Zeiger auf einen polymorphen Typ angewendet wird
  • e einen 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[bearbeiten]
Dynamische Ausnahmespezifikation(bis C++17) gibt an, welche Ausnahmen von einer Funktion geworfen werden (veraltet in C++11) [bearbeiten]
throw-Ausdruck signalisiert einen Fehler und überträgt die Kontrolle an den Fehlerbehandler[bearbeiten]
konvertiert das Argument in ein xvalue, wenn der Move-Konstruktor nicht wirft
(Funktionsvorlage) [edit]