Ausnahmespezifikationen (Throw, noexcept) (C++)

Ausnahmespezifikationen sind ein C++-Sprachfeature, das die Absicht des Programmierers über die Ausnahmetypen angibt, die von einer Funktion weitergegeben werden können. Sie können angeben, dass eine Funktion mithilfe einer Ausnahmespezifikation eine Ausnahme verwenden kann oder nicht beendet werden kann. Der Compiler kann diese Informationen verwenden, um Aufrufe der Funktion zu optimieren und das Programm zu beenden, wenn eine unerwartete Ausnahme die Funktion ausweicht.

Vor C++17 gab es zwei Arten von Ausnahmespezifikationen. Die noexcept-Spezifikation war neu in C++11. Es gibt an, ob die Gruppe potenzieller Ausnahmen, die die Funktion escapen können, leer ist. Die dynamische Ausnahmespezifikation oder throw(optional_type_list) -spezifikation wurde in C++11 veraltet und in C++17 entfernt, mit Ausnahme von throw(), die ein Alias für noexcept(true). Diese Ausnahmespezifikation wurde entwickelt, um zusammenfassende Informationen darüber bereitzustellen, welche Ausnahmen aus einer Funktion ausgelöst werden können, aber in der Praxis wurde festgestellt, dass sie problematisch ist. Die einzige dynamische Ausnahmespezifikation, die sich als etwas nützlich erwiesen hat, war die bedingungslose throw() Spezifikation. Beispielsweise die Funktionsdeklaration:

void MyFunction(int i) throw();

weist den Compiler an, dass die Funktion keine Ausnahmen auslöst. Im /std:c++14 Modus kann dies jedoch zu einem nicht definierten Verhalten führen, wenn die Funktion eine Ausnahme auslöst. Daher empfehlen wir die Verwendung des noexcept Operators anstelle der oben genannten:

void MyFunction(int i) noexcept;

Die folgende Tabelle fasst die Microsoft C++-Implementierung von Ausnahmespezifikationen zusammen:

Ausnahmespezifikation Bedeutung
noexcept
noexcept(true)
throw()
Die Funktion löst keine Ausnahme aus. Im /std:c++14 Modus (die Standardeinstellung noexcept ) und noexcept(true) gleichwertig sind. Wenn eine Ausnahme von einer funktion ausgelöst wird, die deklariert noexceptnoexcept(true)std::terminate oder aufgerufen wird. Wenn eine Ausnahme von einer als im /std:c++14 Modus deklarierten throw() Funktion ausgelöst wird, ist das Ergebnis nicht definiertes Verhalten. Es wird keine bestimmte Funktion aufgerufen. Dies ist eine Divergenz vom C++14-Standard, der zum Aufrufen std::unexpecteddes Compilers erforderlich ist.
Visual Studio 2017, Version 15.5 und höher: Im /std:c++17 Modus , , noexceptnoexcept(true)und throw() alle sind gleichwertig. Im /std:c++17 Modus throw() ist ein Alias für noexcept(true). Im /std:c++17 Modus und später wird, wenn eine Ausnahme von einer Funktion ausgelöst wird, die mit einer dieser Spezifikationen deklariert ist, std::terminate gemäß den Anforderungen des C++17-Standards aufgerufen.
noexcept(false)
throw(...)
Keine Spezifikation
Die Funktion kann eine Ausnahme eines beliebigen Typs auslösen.
throw(type) (C++14 und früher) Die Funktion kann eine Ausnahme vom Typ typeauslösen. Der Compiler akzeptiert die Syntax, interpretiert sie jedoch als noexcept(false). Im /std:c++17 Modus und höher gibt der Compiler die Warnung C5040 aus.

Wenn die Ausnahmebehandlung in einer Anwendung verwendet wird, muss eine Funktion im Aufrufstapel vorhanden sein, die ausgelöste Ausnahmen behandelt, bevor sie den äußeren Bereich einer markierten noexceptFunktion beenden, noexcept(true)oder throw(). Wenn funktionen, die zwischen dem, der eine Ausnahme auslöst, und dem Funktionen, die die Ausnahme behandeln, als , (oder throw() im /std:c++17 Modus) angegeben noexceptnoexcept(true) werden, wird das Programm beendet, wenn die noexcept-Funktion die Ausnahme verteilt.

Das Ausnahmeverhalten einer Funktion hängt von den folgenden Faktoren ab:

  • Der Standardkompilierungsmodus der Sprache wird festgelegt.

  • Ob die Funktion mit C oder C++ kompiliert wird.

  • Welche /EH Compileroption Sie verwenden.

  • Ob die Ausnahmespezifikation explizit angegeben wird.

Explizite Ausnahmespezifikationen sind für C-Funktionen nicht zulässig. Es wird davon ausgegangen, dass eine C-Funktion keine Ausnahmen unterwirft/EHsc, und sie können strukturierte Ausnahmen unter /EHs, oder /EHa/EHac.

In der folgenden Tabelle wird zusammengefasst, ob eine C++-Funktion möglicherweise unter verschiedenen Compiler-Ausnahmebehandlungsoptionen ausgelöst werden kann:

Funktion /EHsc /EHs /EHa /EHac
C++-Funktion ohne Ausnahmespezifikation Ja Ja Ja Ja
C++-Funktion mit noexcept, noexcept(true)throw() oder Ausnahmespezifikation Nein Nein Ja Ja
C++-Funktion mit noexcept(false), throw(...)throw(type) oder Ausnahmespezifikation Ja Ja Ja Ja

Beispiel

// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>

void handler() {
   printf_s("in handler\n");
}

void f1(void) throw(int) {
   printf_s("About to throw 1\n");
   if (1)
      throw 1;
}

void f5(void) throw() {
   try {
      f1();
   }
   catch(...) {
      handler();
    }
}

// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
//   f1();
// }

void __declspec(nothrow) f2(void) {
   try {
      f1();
   }
   catch(int) {
      handler();
    }
}

// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
   f1();
}

int main() {
   f2();

   try {
      f4();
   }
   catch(...) {
      printf_s("Caught exception from f4\n");
   }
   f5();
}
About to throw 1
in handler
About to throw 1
Caught exception from f4
About to throw 1
in handler

Siehe auch

try, throwund catch Anweisungen (C++)
Bewährte Methoden für moderne C++-Methoden für Ausnahmen und Fehlerbehandlung