noexcept in library, wann und wo nutzen
-
Hallo,
ich weiß nicht, ob es state-of-the-art ist, aber mein Senf dazu:
@It0101 sagte in noexcept in library, wann und wo nutzen:
von "extern" genutzt werden kann
-
Noexcept ist Teil des Interfaces und wenn das von "extern" genutzt wird, kannst du es auch nicht ohne weiteres entfernen.
D.h. du kannst auch nicht mehr die Implementierung beliebig ändern. Daher: sparsam einsetzen. -
Ich nutze nur noexcept, sonst keine extra Attributierung
-
Generell nutze ich es eigentlich nur bei move Operatoren, da ich zumindest da weiß, das es Nutzen (Performance) bringen kann. Sonst eigentlich nie und auch bei der Verwendung von Code anderer, gehe ich eigentlich nicht davon aus, dass das immer stimmt, dass eine noexcept-Funktion nicht auch mal ein terminate macht, bzw. dass eine emit-freie Funktion kein noexcept hat.
-
-
noexcept( false ) nutze ich gar nicht. Das ist ja der default und noexcept die Ausnahme, auf ich hinweisen möchte.
-
noexcept(false)
ist nützlich für in seltenen Fällen für Destruktoren, die by default noexcept sind.- Mir ist noexcept in vielen Fällen zu gefährlich, wenn die Funktion nicht super einfach ist, dann muss ich die Exceptionfreiheit beweisen, damit ich nicht die Anwendung abreiße wenn doch eine fliegen kann.
- Moves sollten noexcept sein.
- noexcept ist ein extrem starkes Interface versprechen, dass man in der Zukunft nicht einfach zurücknehmen kann.
-
@5cript sagte in noexcept in library, wann und wo nutzen:
- Mir ist noexcept in vielen Fällen zu gefährlich, wenn die Funktion nicht super einfach ist, dann muss ich die Exceptionfreiheit beweisen, damit ich nicht die Anwendung abreiße wenn doch eine fliegen kann.
Ich lege zwar auch grossen Wert auf Korrektheit der
noexcept
-Angabe, aber wenn ich ehrlich bin, dann haben unbehandelte Exceptions in den meisten Programmen, an denen ich arbeite, nahezu den selben Effekt wie einterminate()
. Die Exceptions werden halt noch vom äußersten Handler gefangen und produzieren eine UI- oder Log-Nachricht. Aber "crashen" tut das Programm dennoch.Zum Glück ist eine fehlerhafter
noexcept
-Specifier nicht UB - das wär übel
-
@Finnegan sagte in noexcept in library, wann und wo nutzen:
aber wenn ich ehrlich bin, dann haben unbehandelte Exceptions in den meisten Programmen, an denen ich arbeite, nahezu den selben Effekt wie ein
terminate()
.Die haben nicht nahezu den Effekt, die rufen terminate() auf.
-
@Finnegan sagte in noexcept in library, wann und wo nutzen:
Zum Glück ist eine fehlerhafter
noexcept
-Specifier nicht UB - das wär übelDas war auch meine Befürchtung. Daher meine Frage.
Aus der Literatur und aus euren Hinweise nehmen ich mir jetzt folgendes mit:
- noexcept ( false ) -> Verwende ich nicht
- noexcept -> Verwende ich bei Ctr/Dtr/Move/Copy sofern möglich
- an anderen Stellen verwende ich noexcept nicht.
Vielen Dank für eure Hinweise. Ihr habt mir geholfen
-
Bei Templates mit einem Member T kann man im allgemeinen davon ausgehen, dass selbst bei Default-Konstruktor noexcept nicht verwendet werden kann, weil man über T niemals eine Aussage treffen kann, richtig?
-
Wie ist eigentlich das Verhalten beim Vererben von z.B. Move-Assignment-Operator?
Wenn ich z.B. von std::vector ableite und dort der Move-Assignment-Operator noexcept ist, ist dann mein compiler-generierter Move-Assignment-Operator ebenfalls automatisch noexcept oder muss ich die Dinger dann selbst definieren nur für das noexcept ?
-
@It0101 sagte in noexcept in library, wann und wo nutzen:
Bei Templates mit einem Member T kann man im allgemeinen davon ausgehen, dass selbst bei Default-Konstruktor noexcept nicht verwendet werden kann, weil man über T niemals eine Aussage treffen kann, richtig?
Ja, außer Du schränkst die Typen mit Hilfe von Concepts ein.
@It0101 sagte in noexcept in library, wann und wo nutzen:
- noexcept -> Verwende ich bei Ctr/Dtr/Move/Copy sofern möglich
Sofern allokiert wird gilt Folgendes:
Nein, bei Move (gilt genauso für Copy, MoveAssignment, CopyAssignment) auf keinen Fall, außer man weiß, dass der Allokator sich gutmütig verhält (z.B. std::allocator), weil jeder dieser Vorgänge bei einem beliebigen Allocator eine Reallokation auslösen kann und die kann prinzipiell eine Exception werfen. Wenn es ein anderer Allokator ist, wird nicht wirklich ein Move gemacht, sondern eine CopyConstruction und eine anschließende Destruktion der Quelle. Das ist Notwendig, weil nur so Daten aus einem besonderen Speicher in einen anderen verschoben werden können. Die Norm ist an dieser Stelle defekt, da sie zwar solche Allokatoren erlaubt, aber andererseits erfordert, dass ein Move O(1) ist und die Zeiger sich nicht verändern.D.h. alle Container der Standard Library haben UB mit Allokatoren die
Allocator<T>::propagate_on_container_move_assigment
alsstd::false_type
definieren.
-
@Tyrdal sagte in noexcept in library, wann und wo nutzen:
@Finnegan sagte in noexcept in library, wann und wo nutzen:
aber wenn ich ehrlich bin, dann haben unbehandelte Exceptions in den meisten Programmen, an denen ich arbeite, nahezu den selben Effekt wie ein
terminate()
.Die haben nicht nahezu den Effekt, die rufen terminate() auf.
Ich meinte das aus der Perspektive des Anwenders eines solchen Programms:
Wenn eine Exception an einer Stelle fliegt, wo ich keine erwartet habe, dann sieht er die eine Meldung á la "WTF? Sorry! Das hätte nicht passieren dürfen. Bye!".
Wenn ich schon einen
noexcept
-Specifier verwende, dann rechne ich dort auch nicht mit einer Exception, d.h. die wird ohnehin nur durch äußersten Handler behandlelt, der obige Nachricht ausgibt.Bei
terminate()
steigt das Programm eben aus, ohne sich vorher höflich zu verabschieden.Natürlich kann es für den Anwender noch Unterschiede bezüglich Ressourcen geben, die nicht vom OS aufgeräumt werden, wenn der Prozess abgeschossen wird. Das ist aber wahrscheinlich eher selten der Fall.
-
Übrigens - ein paar design-philosophische Gedanken, die ich vor einiger Zeit schonmal erwähnt habe: Ich glaube der häufigste Grund, der einem
noexcept
im Wege steht, dürften wohl irgendwelche Speicher-Allokationen sein. Da sollte es - wenn ich nichts übersehen habe - 3 Ursachen geben, weshalb eine Exception fliegen kann:-
Ich denke in einer echten OOM-Situation wird man eine
std::bad_alloc
-Exception ohnehin kaum sinnvoll behandeln können. Ausser vielleicht mit einem sehr speziellen, auf diese Situation zugeschnittenen Handler. -
Ein zu großer Speicherblock und dann einen kleineren versuchen ließe sich sicher auch auf andere Weise behandeln -
new (nothrow)
oder Allocator-Aufrufe in eintry_allocate
-Funktion verpacken, welche die Exception intern behandelt. -
Und schliesslich schlichtweg fehlerhafte Parameter für Allokations-Funktionen. Diese sind m.E. Programmierfehler, die man auch mit Assertions abfangen kann.
Wenn man bei 2+3 auf Exceptions verzichten kann und bei 1 ohnehin nur in seltenen Fällen wirklich behandeln will oder kann, könnte man dort vielleicht auch ein
terminate()
akzeptieren, wodurch vermutlich die allermeisten Funktionen, die heute potentiell werfen könnennoexcept
deklariert werden könnten.Vielleicht könnte man sowas über ein Compiler-Flag Opt-in machen. Besonders wenn ich mir E.12 der C++ Core Guidelines und die Diskussion dazu anschaue ist das keine so abwegige Idee. Die Empfehlung ist dort,
noexcept
nicht nur dann zu verwenden, wenn nachweislich keine Exception fliegen kann, sondern auch, wenn man eine Exception nicht (sinnvoll) behandeln kann oder will und bereit ist, diese wie einen kritischen Hardwarefehler zu betrachten.Da @It0101 allerdings wie eingangs erwähnt an einer Bibliothek arbeitet, möchte ich das hier nur als allgemeinen Diskussionsbeitrag verstanden wissen. Ob ein
terminate()
akzeptabel ist, ist eine Entscheidung, die dem Anwender einer Bibliothek vorbehalten bleiben sollte. In Bibliotheks-Code würde ich auch "nachweislich keine Exception" als Maßstab empfehlen.
-
-
@Finnegan sagte in noexcept in library, wann und wo nutzen:
- Ich denke in einer echten OOM-Situation wird man eine
std::bad_alloc
-Exception ohnehin kaum sinnvoll behandeln können. Ausser vielleicht mit einem sehr speziellen, auf diese Situation zugeschnittenen Handler.
Dank Memory Overcommitment wirst Du in einem echten OOM-Fall gar keine Exception sehen, da erst bei Zugriff auf die Pages das OS feststellt, dass es nicht mehr ausreichend RAM hat. Auf einem UNIX/Linux siehst Du da einen SegFault, und das Programm kann da gar nichts mehr machen. Was man darüber nur abfangen kann, sind unrealistisch große Speicheranforderungen, da gibt das OS noch eine negative Rückmeldung.
- Ich denke in einer echten OOM-Situation wird man eine
-
@john-0 sagte in noexcept in library, wann und wo nutzen:
@Finnegan sagte in noexcept in library, wann und wo nutzen:
- Ich denke in einer echten OOM-Situation wird man eine
std::bad_alloc
-Exception ohnehin kaum sinnvoll behandeln können. Ausser vielleicht mit einem sehr speziellen, auf diese Situation zugeschnittenen Handler.
Dank Memory Overcommitment wirst Du in einem echten OOM-Fall gar keine Exception sehen, da erst bei Zugriff auf die Pages das OS feststellt, dass es nicht mehr ausreichend RAM hat. Auf einem UNIX/Linux siehst Du da einen SegFault, und das Programm kann da gar nichts mehr machen. Was man darüber nur abfangen kann, sind unrealistisch große Speicheranforderungen, da gibt das OS noch eine negative Rückmeldung.
Ja, vielleicht noch durch vom System forcierte Speicher-Limits, oder wenn man eine Malloc-Implementierung verwendet, der man eine Obergrenze mitgeben kann. Eigentlich ein Argument mehr, dass man in dem Fall auch ein
terminate()
akzeptabel sein könnte. Schlechter als ein SegFault ist das nicht.Sicher gibts auch Szenarios, wo man die Exceptions haben will, aber die Menge an Funktionen, die
noexcept
sein könnten, wennbad_alloc
wegfällt und fast überall z.B. beistd::move_if_noexcept
gemoved werden kann ist nicht zu verachten.Würde mich jedenfalls mal interessieren, was sich damit im Schnitt herausholen ließe. Besonders bei größerem Legacy-Code, der gar nichts
noexcept
deklariert und viel in Containerklassen herumschiebt. Zumindest wenn die Klassen überhaupt effizient gemoved werden können, z.B. mit vielenstd::string
oder std-Container Daten-Membern.
- Ich denke in einer echten OOM-Situation wird man eine