- Es gibt viele Beispiele für Funktionen, von denen ich weiß, dass sie niemals ausgelöst werden, für die der Compiler dies jedoch nicht selbst bestimmen kann. Sollte ich in all diesen Fällen keine Ausnahme an die Funktionsdeklaration anhängen?
noexcept
ist schwierig, da es Teil der Funktionsschnittstelle ist. Insbesondere wenn Sie eine Bibliothek schreiben, kann Ihr Client-Code von der noexcept
Eigenschaft abhängen . Es kann schwierig sein, es später zu ändern, da Sie möglicherweise vorhandenen Code beschädigen. Dies ist möglicherweise weniger problematisch, wenn Sie Code implementieren, der nur von Ihrer Anwendung verwendet wird.
Wenn Sie eine Funktion haben, die nicht ausgelöst werden kann, fragen Sie sich, ob sie bleiben soll noexcept
oder ob dies zukünftige Implementierungen einschränken würde. Beispielsweise möchten Sie möglicherweise eine Fehlerprüfung für unzulässige Argumente einleiten, indem Sie Ausnahmen auslösen (z. B. für Komponententests), oder Sie sind auf anderen Bibliothekscode angewiesen, der die Ausnahmespezifikation ändern kann. In diesem Fall ist es sicherer, konservativ zu sein und wegzulassen noexcept
.
Wenn Sie jedoch sicher sind, dass die Funktion niemals ausgelöst werden sollte und es richtig ist, dass sie Teil der Spezifikation ist, sollten Sie sie deklarieren noexcept
. Beachten Sie jedoch, dass der Compiler keine Verstöße erkennen kann, noexcept
wenn sich Ihre Implementierung ändert.
- In welchen Situationen sollte ich bei der Verwendung von noexcept vorsichtiger sein und in welchen Situationen kann ich mit dem implizierten noexcept (false) davonkommen?
Es gibt vier Funktionsklassen, auf die Sie sich konzentrieren sollten, da sie wahrscheinlich den größten Einfluss haben:
- Verschiebungsoperationen (Verschiebungszuweisungsoperator und Verschiebungskonstruktoren)
- Swap-Operationen
- Speicherfreigaben (Operator löschen, Operator löschen [])
- Destruktoren (obwohl diese implizit sind, es
noexcept(true)
sei denn, Sie machen sie noexcept(false)
)
Diese Funktionen sollten im Allgemeinen vorhanden sein noexcept
, und es ist sehr wahrscheinlich, dass Bibliotheksimplementierungen die noexcept
Eigenschaft verwenden können. std::vector
Kann beispielsweise nicht werfende Bewegungsoperationen verwenden, ohne starke Ausnahmegarantien zu opfern. Andernfalls muss auf das Kopieren von Elementen zurückgegriffen werden (wie in C ++ 98).
Diese Art der Optimierung erfolgt auf algorithmischer Ebene und basiert nicht auf Compiler-Optimierungen. Dies kann erhebliche Auswirkungen haben, insbesondere wenn das Kopieren der Elemente teuer ist.
- Wann kann ich realistisch mit einer Leistungsverbesserung nach Verwendung von noexcept rechnen? Geben Sie insbesondere ein Beispiel für Code an, für den ein C ++ - Compiler nach dem Hinzufügen von noexcept besseren Maschinencode generieren kann.
Der Vorteil noexcept
gegenüber keiner Ausnahmespezifikation oder throw()
ist, dass der Standard den Compilern mehr Freiheit beim Abwickeln von Stapeln ermöglicht. Selbst in diesem throw()
Fall muss der Compiler den Stapel vollständig abwickeln (und dies in der exakten umgekehrten Reihenfolge der Objektkonstruktionen).
In diesem noexcept
Fall ist dies jedoch nicht erforderlich. Es ist nicht erforderlich, dass der Stapel abgewickelt werden muss (der Compiler darf dies jedoch weiterhin tun). Diese Freiheit ermöglicht eine weitere Codeoptimierung, da dadurch der Aufwand verringert wird, den Stapel immer wieder abwickeln zu können.
Die verwandte Frage zu noexcept, Abwickeln des Stapels und Leistung geht näher auf den Overhead ein, wenn das Abwickeln des Stapels erforderlich ist.
Ich empfehle auch das Scott Meyers-Buch "Effective Modern C ++", "Punkt 14: Deklarieren Sie Funktionen, die keine Ausnahmen sind, wenn sie keine Ausnahmen ausgeben", um sie weiter zu lesen.
move_if_nothrow
(oder whatchamacallit), wird eine Leistungsverbesserung feststellen, wenn es keine Ausnahmeverschiebung gibt.