Wann noexcept?



  • Im Grunde muss jeder Destructor noexcept sein, weil es sonst beim Stack Unwinding zu Problemen kommen kann. std::unexcept hat man leider aus der Norm geworfen, aber das Stack Unwinding Problem in Zusammenhang mit Exception Handling ist noch immer nicht gelöst.



  • @john-0 sagte in Wann noexcept?:

    Im Grunde muss jeder Destructor noexcept sein, weil es sonst beim Stack Unwinding zu Problemen kommen kann. std::unexcept hat man leider aus der Norm geworfen, aber das Stack Unwinding Problem in Zusammenhang mit Exception Handling ist noch immer nicht gelöst.

    Glücklicherweise sind diese bereits implizit noexcept:

    Every function in C++ is either non-throwing or potentially throwing

    • potentially-throwing functions are:
      ...
      • functions declared without noexcept specifier except for
        • destructors unless the destructor of any potentially-constructed base or member is potentially-throwing (see below)

    Man muss sich also bei Destruktoren erst dann Gedanken um einen eventuellen noexcept-Specifier machen, wenn man auch tatsächlich aus dem Destruktor herauswerfen will - da hatte ich bisher zum Glück noch nie einen Anlass zu.

    Das ist gut, da ich ja letztendlich einfach nur möglichst wenig noexcept schreiben will, ohne dabei der Effizienz oder Korrektheit zu schaden.



  • @Finnegan sagte in Wann noexcept?:

    Glücklicherweise sind diese bereits implizit noexcept:

    Nein, das ist nicht der Fall, Du solltest Dir N3797 §15.4 Absatz 14 durchlesen. Der Destructor ist nur dann implizit noexcept, wenn er nur Funktionen aufruft die noexcept sind.


  • Mod

    @john-0 Das ist falsch/irreführend wiedergegeben (und Finnegan hat die relevante Passage doch sowieso mitzitiert). Der Destruktor leitet seine exception-specification von den Destruktoren der Subobjekte ab, deren unmittelbaren Aufruf er mit einfasst. Nicht etwa von allen Funktionen, die er potenziell aufruft.

    Da Destruktoren eben standardmäßig noexcept sind, wird letztlich auch jeder Destruktor noexcept bleiben, auch dann, wenn der Funktionskörper einen throw-Ausdruck beinhaltet, oder eine gewöhnliche, werfende Funktion aufruft.

    Bisjemand auf die bekloppte Idee kommt, tatsächlich noexcept(false) an einen Destruktor zu klatschen. Was ich noch nie gesehen habe.

    Wobei ich das Gefühl habe, dass sogar das oft zu noexcept(true) führen würde, weil die Funktionen, die gewöhnlich in Destruktoren Anwendung finden, ja doch noexcept sind, z.B. operator delete, Destruktoren von Standardbibliotheks-Klassen, etc. was natürlich rekursiv auf alle daraus aufgebauten Klassen zutrifft.

    @Finnegan Was ich bzgl. profilieren meinte, war eigentlich, dass wir (d.h. im Forum) mal eine mini-Studie machen sollten, in der wir ein paar solcher Fälle auf Optimierungen untersuchen. Einfach um ein Gefühl dafür zu bekommen, inwiefern das praktisch einen Unterschied macht.



  • @Columbo sagte in Wann noexcept?:

    @john-0 Das ist falsch/irreführend wiedergegeben (und Finnegan hat die relevante Passage doch sowieso mitzitiert).

    Da ich die aktuelle ISO Norm nicht zur Hand habe, habe ich auf den letzten Draft N3797 verwiesen und da findet sich folgender Absatz darin. Der entscheidende Absatz fett markiert.

    Als die relevante Stelle als Zitat
    An inheriting constructor (12.9) and an implicitly declared special member function (Clause 12) have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions.



  • @Columbo @john-0 Ich habe mir die referenzierte Passage gerade im aktuellen PDF durchgelesen und das Standardesisch auch so verstanden, dass für ein implizites noexcept auch jede aufgerufene Funktion noexcept sein muss. Das kann ja durchaus schnell passieren - z.B. gehen wahrscheinlich irgendwelche release-Funktionen einer C-Library-API meistens nicht wirklich als noexcept durch (sowas hätte ich z.B. öfter mal in den Destruktoren, die ich so schreibe).

    In dem Fall wäre mir tatsächlich ein explizites noexcept lieber, da ich bei dem dann eventuell auftretenden terminate-Call unmissverständlich merke, dass da etwas faul ist.

    Ich will eigentlich keine Exceptions in Destruktoren und denke, die sind auch immer irgendwie vermeidbar.

    @Columbo So eine "Mini-Studie" wäre sicher mal interessant. Ich poste hier zwar immer wieder gerne eigene Experimente, weiss aber nicht, ob ich Zeit für sowas finde. Mal schauen. Move-Konstruktor ohne noexcept und std::vector ist da sicher ne tief hängede Frucht, da lassen sich bestimmt gruselige Beispiele mit konstruieren, wenn Kopien nur teuer genug sind 😉

    Edit: Was mich bei dem Standard-Absatz allerdings stutzig macht, ist, dass nur von implicitly declared special member function/destructor die Rede ist. Diese sollten ja direkt eigentlich nur die Destruktoren der Basisklassen/Member aufrufen. Dafür ist das aber ganz schön umständlich formuliert 😉 ... uns geht es doch hier eher um das implizite noexcept von expliziten Destruktoren, oder?


  • Mod

    @Finnegan @john-0 Das ist doch gerade die Idee, dass man werfende Funktionen aufrufen kann, aber der Destruktor noexcept bleibt. Weil die beste allgemeine Strategie ist, dass eine Exception im Destruktor zu terminate führt.

    Das wording, dass von @john-0 zitiert worden ist, ist defekt, und wurde schon vor 8 Jahren zu korrigieren versucht: https://wg21.cmeerw.net/cwg/issue1351

    Wer mir immer noch nicht glaubt: https://coliru.stacked-crooked.com/a/054416897d790050



  • @Columbo sagte in Wann noexcept?:

    @Finnegan @john-0 Das ist doch gerade die Idee, dass man werfende Funktionen aufrufen kann, aber der Destruktor noexcept bleibt.

    Bitte lies Dir den Absatz ganz genau noch einmal durch!


  • Mod

    @john-0 Der aktuelle WD ist viel weiter als N3797 (welches von vor C++14 ist). Und die zitiere Passage ist, sowie @Finnegan aufgezeigt hat, doch gar nicht widersprüchlich, wegen implicitly declared, was meine Erklärung bestätigt. Wo liegt noch das Problem?



  • Sehe ich auch so wie @Finnegan: hier geht es um das implizite noexcept von expliziten Destruktoren (nicht um implizite Destruktoren, wie in dem Standard-Absatz beschrieben).



  • @Columbo sagte in Wann noexcept?:

    @john-0 Der aktuelle WD ist viel weiter als N3797 (welches von vor C++14 ist).

    Entschuldigung, die Webseite der C++ Working Group ist extrem schlecht. Wenn man da unter ISO 14882:2017 schaut, gibt es einen Link auf die ISO Publikation und direkt daneben einen Verweise auf N3797 und nicht etwa den passenden letzten Draft vor ISO/IEC 14882:2017.

    In letzten Draft vor ISO/IEC 14882:2017 ist es so wie von Dir beschrieben. cppreference.com dokumentiert leider nicht was aktueller Norm ist und was aktueller Working Draft ist.

    Leider kann sich die C++ Working Group nicht dazu durchringen, die offiziellen Normen frei zu publizieren, wie das etwa die Ada Conformty Assessment Authority macht. Dazu gibt es von Ada immer frei den Rationale dazu, in dem man nachlesen kann, weshalb etwas gemacht wurde. Das ist absolut vorbildlich und angesichts der Tatsache, dass C++ bedeutender ist um so unverständlicher.


Anmelden zum Antworten