Wann noexcept nutzen?
-
wannnoexcept schrieb:
ich habe in meiner Applikation momentan mehrere Model-Klassen deren Methoden meistens keine Exception werfen. Ist es sinnvoll diese mit noexcept zu deklarieren?
"no" heißt nicht "selten", sondern "keine".
Das sollte man nicht einfach überall hinschreiben, wo aktuell keine Exceptions fliegen. Das kann sich schließlich mal ändern.
Hauptsächlich ist
noexceptfür Konstruktoren, Zuweisungsoperatoren und den Destruktor gedacht, um ausnahmesicheren, generischen Code effizienter zu machen. Zum Beispiel kannstd::vectorbeim Vergrößern unnötige Kopien vermeiden, wenn die Elementenoexcept-movable sind.Man kann
noexceptzur Mikrooptimierung benutzen, weil der Compiler unter Umständen besseren Code generieren kann, wenn ein Aufruf garantiert nicht wirft.(Sorry, den muss ich einfach hinschreiben: "Ohne Ausnahme: noexcept lässt keine Exceptions zu".)
Ruvi schrieb:
Soweit ich das verstanden habe kann der Compiler dann Optimierungen vornehmen die er ansonsten nicht vornehmen könnte ABER sollte dort doch eine Exception rauskommen dann landest, wenn ich mich richtig erinnere im UB-Land.
Nö, das endet mit
std::terminate. Der Stack darf vom Compiler abgeräumt werden, muss aber nicht.
-
TyRoXx schrieb:
Nö, das endet mit
std::terminate. Der Stack darf vom Compiler abgeräumt werden, muss aber nicht.Ich kann es leider nicht testen VS hat noexcept noch nicht ordentlich implementiert, also wird std::terminate aufgerufen sobald eine Exception von einer noexcept Funktion geworfen wird?
Oder meinst Du, dass die Exception die dort geworfen wird wahrscheinlich nicht gefangen wird und durch die Exception selbst std::terminate aufgerufen wird?
-
Ruvi schrieb:
Ich kann es leider nicht testen VS hat noexcept noch nicht ordentlich implementiert, also wird std::terminate aufgerufen sobald eine Exception von einer noexcept Funktion geworfen wird?
Oder meinst Du, dass die Exception die dort geworfen wird wahrscheinlich nicht gefangen wird und durch die Exception selbst std::terminate aufgerufen wird?Normalerweise geht das Programm bei einer exception dazu ueber, den ganzen Stack abzuraeumen und alle Destruktoren aufzurufen, bis man beim entsprechenden catch ist. Bei noexcept kann einfach sofort beendet werden und man spart sich dadurch die ganze Fehlerbehandlung und macht ggf. den Code schneller.
-
Hö?
D.h. bei noexcept werden Destruktoren nicht mehr aufgerufen?
Das scheint mir jetzt auf den ersten Blick etwas, naja, komisch.
-
Finde ich gar nicht komisch. Bestenfalls kann der Stack des Threads abgeräumt werden, in dem die Exception auftritt. Wenn du das wirklich brauchst, kann du immer noch mit
catchdas Aufräumen erzwingen, wo du es haben möchtest.Das
terminatebeinoexceptist überhaupt nicht dazu gedacht das Programm "normal" zu beenden. Es soll nur ein definiertes Verhalten schaffen, wo keine sinnvolle Fehlerbehandlung mehr möglich ist.Es ist ein wenig schade, dass man
noexceptnicht strenger gemacht hat: Einenoexcept-Funktion sollte nur ausnoexcept-Statements bestehen dürfen. Der nächste logische Schritt wäre dannnoexcept(auto), das bei Templates undinline-Funktionen angewendet werden könnte.
-
Eine
noexcept-Funktion sollte nur ausnoexcept-Statements bestehen dürfen.I.e. folgender Code
void foo() noexcept { try {somethingThatThrows();} catch(...) {std::cerr << "eww.";} }Ist verboten? Gut, okay. Wir führen eine Regelung ein die es uns erlaubt beliebigen Code in einem
try-Block unterzubringen - aber nur wenn dieser von einemcatch(...) { /*...*/ }gefolgt wird, welcher selbst tatsächlich nur ausnoexceptStatements besteht (wobei die obige Regel rekursiv anwendbar ist). Was ist mitvoid foo() noexcept { try {somethingThatThrowsInt();} catch(int) {std::cerr << "eww.";} }? Ups,
catch(...)fehlt. Schnell ergänzen:void foo() noexcept try { try {somethingThatThrowsInt();} catch(int) {std::cerr << "eww.";} } catch(...) {std::terminate();}Aber Moment, das letzte
catch(...)in dieser Form können wir doch automatisch anhängen lassen? ...
-
Ich finde es nicht gut, wenn der Compiler Code generiert, der das Programm abschießt, wenn er das Problem auch schon beim Übersetzen hätte erkennen können. Das
terminate-Verhalten hätte man sehr gut in der Standardbibliothek umsetzen können. Da wäre keine Compiler-Magie notwendig gewesen.namespace std { template <class F, class ...Args> auto terminate_on_exception(F &&f, Args &&...args) noexcept { try { return std::forward<F>(f)(std::forward<Args>(args)...); } catch (...) { std::terminate(); } } } void foo() noexcept { std::terminate_on_exception([] { try {somethingThatThrowsInt();} catch(int) {std::cerr << "eww.";} }); }
-
Und
newwillste auch jedes mal kapseln?PS: Da solltest du eigentlich eher
invokeanwenden.
-
Ruvi schrieb:
Wenn aus diesen Methoden garantiert keine Exceptions geworfen werden können (mit anderen Worten aus dem gesamten Call-Stack der jeweiligen Funktion) sehe ich persönlich keinen Grund warum Du nicht noexcept ranschreiben solltest.
Doch, es gibt einen Grund.
noexceptist zumindest fraglich bei Funktionen, die Parameter "by value" nehmen, deren Copy-Konstruktor werfen kann (bzw. allgemein: die einen Konstruktor haben der etwas werfen könnte).Aus Sicht des Standard kann die Funktion zwar trotzdem noch
noexceptsein, aber nur weil der Aufruf der nötigen Konstruktoren für Parameter aus Sicht des Standards beim Aufrufer passiert.
Und danoexceptfür eine solche Funktion mMn. klar das principle of least astonishment verletzt, sollte man es weglassen.
(Was bringt es schon dass die Funktionoexceptist, wenn man sie nichtnoexceptaufrufen kann?)
-
ps: Einige Funktionen, wie z.B. auto-generierte Konstruktoren, haben ja einen automagischen
noexcept("true, wenns nicht gelogen ist, und sonst halt false")specifier.
Wäre es nicht praktisch das auch für inline-definierte Funktionen zu erlauben?
Alainline void Fun() noexcept(auto) { // Do stuff that might or might not all be noexcept(true) }EDIT: OK, gibt eh schon (lange) ein Proposal: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3207.htm
Weiss jemand was der Status davon ist?
Das darin beschriebene "Problem 1" ist mMn. keines, wenn man es auf inline definierte Funktionen einschränkt.
Und das darin beschriebene "Problem 2" ist glaube ich auch schon gelöst, danoexceptSpecs laut Standard erst nach der Overload-Resolution angeguckt werden (oder hab ich da falsche Infos/etwas falsch verstanden?).EDIT2: Sorry TyRoXx, hab jetzt erst gesehen dass du auch bereits
noexcept(auto)angesprochen hattest.
-
Arcoth schrieb:
Und
newwillste auch jedes mal kapseln?Du meinst
make_uniqueundmake_shared?
"Jedes mal"? Es geht hier nur umnoexcept-Funktionen.Arcoth schrieb:
PS: Da solltest du eigentlich eher
invokeanwenden.Stimmt, aber ich bin gedanklich noch nicht im Jahr 2017 angekommen.
hustbaer schrieb:
noexceptist zumindest fraglich bei Funktionen, die Parameter "by value" nehmen, deren Copy-Konstruktor werfen kann (bzw. allgemein: die einen Konstruktor haben der etwas werfen könnte).Ich weiß nicht, wie praktikabel das ist, aber ein Compiler könnte da warnen.
-
Ne Warning wäre mMn. sehr praktikabel

Oder meinst du jetzt wie schwer/einfach das im Compiler umsetzbar ist (was ich auch nicht weiss, schätze aber das kann kein ganz grosses Problem sein)?