2010-11-13 2 views
6

Wenn ich das nicht-werfende Swap-Idiom implementiere, sollte ich throw() verwenden?Sollte ich throw() bei der Implementierung von Non-Throwing-Swaps verwenden?

namespace A 
{ 
    struct B 
    { 
    void swap(B& other) throw() 
    { /* fancy stuff that doesn't throw */ } 
    }; 

    void swap(B& lhs, B& rhs) throw() 
    { lhs.swap(rhs); } 
} 

namespace std 
{ 
    template<> 
    void swap(A::B& lhs, A::B& rhs) throw() 
    { lhs.swap(rhs); } 
} 

Insbesondere Ich sorge mich um die throw() Spezifikation auf die Spezialisierung der std::swap setzen.

Bonus Frage:
Ist die Antwort anders, wenn C++ 0x noexcept Schlüsselwort verwendet?

+0

Sie können diese drei Swap-Deklarationen bis auf eins vereinfachen: 'struct B {Freund void swap (B & a, B & b) {/ * ...* /}}; ', dann müssen die Benutzer einfach swap als' using std :: swap; swap (a, b); ', die Ihren Swap durch ADL einzieht (oder sie können boost :: swap verwenden, was dies bereits tut). Dies macht insbesondere die Sorge um eine Ausnahmespezifikation für std :: swap überflüssig, wenn Sie sich entscheiden, eine für Ihren Swap zu verwenden. –

Antwort

4

In C++ 03 können Sie es dort setzen, aber wenn es wahr ist, dass die schicken Sachen nicht werfen, ist es im Grunde nur Dokumentation. Es kann sich auf die Leistung auswirken oder nicht, indem das Äquivalent von try/catch(...) { std::unexpected(); } um Aufrufe der Funktion hinzugefügt wird: Es hängt von der Implementierung ab, ob dies ohne Beeinträchtigung der Leistung möglich ist.

Wenn Sie planen, die noexceptOperator (5.3.7) in C++ 0x, dann mit Spezifikationen nicht werfen Ausnahme wert wird es plötzlich zu verwenden, so dass der Bediener die „richtige“ Antwort gibt. Ich weiß nicht wirklich, was der Operator noexcept ist, aber wenn es einen cleveren generischen Einsatz dafür gibt, zum Beispiel Algorithmen, die effizienter werden, wenn etwas nicht geworfen wird, dann wird es wohl notwendig sein, Funktionen als nicht zu markieren - um zu bekommen, was auch immer der Vorteil ist.

Zum Beispiel:

void foo() noexcept; 
void bar(); 

template <void(*FUNC)()> 
void generic_thing() { 
    if (noexcept(FUNC()) { 
     // this won't throw, perhaps we can somehow take advantage of that 
     FUNC(); 
    } else { 
     // this might throw 
     FUNC(); 
    } 
} 

Old style Ausnahme-Spezifikationen (dynamic-Ausnahme-Spezifikation) sind in C++ 0x veraltet (und sinnlos in C++ 03).

+0

Der Operator 'noexcept' benötigt keinen Ausdruck. dh 'noexcept == noexcept (true)'. siehe § 15.4-1 im FCD. –

+1

@caspin: Der 'noexcept' * Operator * tut das. Es ist ein unärer Operator, der in 5.3.7 definiert ist, und er testet, ob ein Ausdruck * werfen kann, ohne ihn zu bewerten. Vereinbart, dass das noexcept * Schlüsselwort *, das in einer * exception-specification * verwendet wird, keinen Ausdruck benötigt. Ich bin mir nicht sicher, warum ich '(true)' da hineinlege. –

+0

gibt der 'noexcept' Operator einen' bool constexpr' zurück? Ich würde sagen, es ist definitiv möglich und daher sollte es möglich sein, dies als Template-Parameter zu verwenden ... Obwohl ich vermute, dass jeder Compiler, der seinen Wert wert ist, den toten Zweig eliminiert und das "if" hier komplett entfernt, wenn das Ergebnis berechenbar ist zur Kompilierzeit ... und warum sollte es nicht? –

1

Realistisch, nein. Exception-Spezifikationen waren eine schöne Idee, aber die Realität von ihnen war, dass sie mehr als nur die Mehrheit des Codes behindert. Niemand benutzt sie und sie sind in C++ 0x veraltet. Verbringe keine Zeit damit, Ausnahmenspezifikationen in modernem C++ zu schreiben.

+1

Ich bin mir bewusst, dass Exception-Spezifikationen eine schlechte Idee und veraltet sind. Ich dachte 'throw()' (die leere Spezifikation) war die Ausnahme von der Regel. Ich dachte 'noexcept' war nur eine aktualisierte Version von' throw() 'mit der Fähigkeit,' throw() 'über einen Kompilierzeit-Ausdruck ein- und auszuschalten. –

3

Es ist keine gute Idee, nein. Der Grund dafür ist, dass der Standard weiß, dass es manchmal unmöglich ist, die throw() Korrektheit zu erzwingen, die throw() für in erster Linie gebracht wurde. Insbesondere bei Vorlagen, bei denen je nach Instanziierung eine Ausnahme ausgelöst werden kann oder nicht, ist es ohne die Template-Instanziierung unmöglich, die Richtigkeit zu überprüfen. Der Standard erfordert daher nicht einmal Compiler, um den Ausnahmespezifizierer zu erzwingen; in der Tat verbietet es eine Implementierung von der Ablehnung eines Ausdrucks "nur weil, wenn es ausgeführt wird, wirft es oder könnte eine Ausnahme werfen, die die enthaltende Funktion nicht erlaubt" (15.4/11).

Wenn throw() keinen anderen Effekt als Dokumentation hat, sollte es auskommentiert werden; Auf diese Weise besteht zumindest keine Gefahr, den menschlichen Beobachter in die Irre zu führen.

Der Fall mit noexcept ist ganz anders. noexcept kommt aus einem anderen Grund; das der Leistung. Wenn Sie können garantieren, dass der Compiler keine Ausnahme ausgelöst wird, dann ermöglicht der Compiler selbst einige Optimierungen durchzuführen. Aber Sie müssen die Prüfungen selbst durchführen. Also, wenn in der Tat swap() nicht wirft (und es sollte nicht; das ist seine Existenzberechtigung) dann ist die Angabe noexcept eine gute Idee.

+0

"Der Standard erfordert daher nicht einmal Compiler, um den Ausnahmespezifizierer zu erzwingen." - Darüber hinaus verlangt der Standard, dass Compiler * keine Exception-Spezifikationen (15.4/10) durchsetzen. –

+0

@SteveJessop richtig, danke für die Korrektur. Es ist übrigens 15.4.11. – wilhelmtell

+0

@wilhelm: war nicht als Korrektur gedacht, nur zusätzliche Information :-). Meine Nummerierung stammt von "ISO/IEC 14882: 2003 (E)", d.h. C++ 03. Wie du sagst, ist es 15,4/11 in der C++ 0x FCD. –

Verwandte Themen