Unterschied zwischen C ++ 03 throw () -Spezifizierer C ++ 11 noexcept

100

Gibt es einen Unterschied zwischen throw()und noexceptaußer der Überprüfung zur Laufzeit bzw. zur Kompilierungszeit?

Dieser Wikipedia C ++ 11-Artikel schlägt vor, dass die C ++ 03-Wurfspezifizierer veraltet sind.
Warum ist es in der noexceptLage, all das zur Kompilierungszeit abzudecken?

[Hinweis: Ich habe diese Frage und diesen Artikel überprüft , konnte jedoch den soliden Grund für die Ablehnung nicht ermitteln.]

iammilind
quelle
7
Zu diesem Accodring schönen Artikel auch noexceptkönnen Laufzeitprüfungen anfallen. Der wesentliche Unterschied zwischen ihnen , dass der Bruch ist noexceptUrsachen , std::terminatewährend brechen throwUrsachen std::unexpected. Auch ein etwas anderes Abwickelverhalten des Stapels in diesen Fällen.
Fiktik
Bei einigen Ausnahmespezifikationen wird nichts "Kompilierungszeit" überprüft, bei anderen wird "Laufzeit" überprüft. Es ist nur ein Mythos, der von Gegnern von C ++ - Ausnahmespezifikationen erstellt wurde.
Neugieriger

Antworten:

129

Ausnahmespezifizierer wurden abgelehnt, da Ausnahmespezifizierer im Allgemeinen eine schreckliche Idee sind . noexceptwurde hinzugefügt, weil es die einigermaßen nützliche Verwendung eines Ausnahmespezifikators ist: zu wissen, wann eine Funktion keine Ausnahme auslöst. Somit wird es eine binäre Wahl: Funktionen, die werfen und Funktionen, die nicht werfen.

noexceptwurde hinzugefügt, anstatt nur alle Wurfspezifizierer zu entfernen, außer throw()weil dies noexceptleistungsfähiger ist. noexceptkann einen Parameter haben, dessen Kompilierungszeit in einen Booleschen Wert aufgelöst wird. Wenn der Boolesche Wert wahr ist, bleiben die noexceptSticks. Wenn der Boolesche Wert falsch ist, noexceptbleibt der nicht hängen und die Funktion kann werfen.

So können Sie etwa Folgendes tun:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Wirft CreateOtherClassAusnahmen? Es könnte sein, wenn Tder Standardkonstruktor dies kann. Wie erzählen wir? So was:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Daher CreateOtherClass()wird iff den Standardkonstruktor des angegebenen Typs auslösen. Dies behebt eines der Hauptprobleme bei Ausnahmespezifizierern: ihre Unfähigkeit, den Aufrufstapel weiterzugeben.

Das kann man nicht machen throw().

Nicol Bolas
quelle
+1 Nützliche Antwort, für mich jedenfalls. Ich suche immer noch nach einer Antwort, die besagt, warum ich sie verwenden möchte noexcept. Ich habe niemals einen throw()Spezifizierer verwendet und versuche festzustellen, ob noexcepttatsächlich ein Nutzen erzielt wird (außer der vom Compiler geprüften Dokumentation).
hmjd
1
@NicolBolas stimmen zu. Wenn jedoch noexcept eine Garantie wäre, könnte der Compiler prüfen, ob eine Funktion einen Destruktor auslösen kann oder nicht. Auf diese Weise kann ein Programmierer gewarnt werden, dass eine Funktion nicht vorhanden ist oder nicht.
Alex
2
@NicolBolas die Laufzeitaufrufe std::terminate. Das ist viel schlimmer ! Code kann sich in Releases einschleichen, deren Funktionen markiert sind, noexcept und zur Laufzeit (dh bei Kundenstandorten) werden Verstöße erkannt. Ich meinte die Compiler - Garantien Code zu generieren , die nicht werfen Ausnahmen in dem ersten Platz.
Alex
2
@NicolBolas: Ein weiterer bemerkenswerter Unterschied. Wenn eine Funktion markiert throws()ist, muss der Stapel, wenn eine Ausnahme ausgelöst wird, bis zum Umfang dieser Funktion abgewickelt werden (damit alle automatischen Variablen in der Funktion zerstört werden). An diesem Punkt terminate()wird (via unexpected()) aufgerufen . Wenn eine Funktion markiert ist und noexceptdann eine Ausnahme ausgelöst wird, wird terminate aufgerufen (das Abwickeln des Stapels ist ein implementierungsdefiniertes Detail).
Martin York
33

noexcept wird beim Kompilieren nicht überprüft.

Eine Implementierung darf einen Ausdruck nicht nur ablehnen, weil er bei seiner Ausführung eine Ausnahme auslöst oder auslösen könnte, die die enthaltende Funktion nicht zulässt.

Wenn eine Funktion deklariert wird noexceptoder throw()versucht, eine Ausnahme terminateauszulösen, besteht der einzige Unterschied darin, dass man aufruft und die anderen Aufrufe unexpectedund der letztere Stil der Ausnahmebehandlung effektiv veraltet sind.

CB Bailey
quelle
Wenn eine virtuelle Funktion throw()/ noexcepthat, stellen Sie bei der Überprüfung der Kompilierungszeit sicher, dass auch ein Overrider vorhanden ist.
Neugieriger
2

std::unexpected() wird von der C ++ - Laufzeit aufgerufen, wenn eine dynamische Ausnahmespezifikation verletzt wird: Eine Ausnahme wird von einer Funktion ausgelöst, deren Ausnahmespezifikation Ausnahmen dieses Typs verbietet.

std::unexpected() kann auch direkt aus dem Programm aufgerufen werden.

Ruft in beiden Fällen std::unexpectedden aktuell installierten auf std::unexpected_handler. Der Standard std::unexpected_handlernennt std::terminate.

ma13
quelle