2010-10-04 13 views
9

Lets sagen, dass ich diese Ausnahmeklasse haben:Kann jemand Rvalue-Referenzen in Bezug auf Ausnahmen erklären?

struct MyException : public std::exception 
{ 
    MyException(const std::exception &exc) : std::exception(exc) 
    { 
     cout << "lval\n"; 
    } 
    MyException(std::exception &&exc) : std::exception(std::forward<std::exception>(exc)) 
    { 
     cout << "rval\n"; 
    } 
}; 

... 
... 

try 
{ 
    throw std::exception("Oh no!"); 
    // above is rvalue since it's got no name, what if the throw is made as 
    // std::exception lvalExc("Oh wierd!"); 
    // throw lvalExc; 
    // if the throw is made thus, how can it be caught by catch(std::exception &&exc)? 
} 
catch(std::exception &&rValRef) 
{ 
    cout << "rValRef!\n"; 
    throw MyException(std::forward<std::exception>(rValRef)); 
} 

Als ich versuchte, nach Wert oder (const) lvalue ref zu fangen. Der Compiler sagt, dass diese Fälle bereits von der Klausel rvalue ref catch behandelt werden, was verständlich ist, als Ausnahme ist eine xvalue und vielleicht ist der beste Weg, einen xvalue zu fangen, ein rvalue ref (korrigiere mich, wenn ich falsch liege). Aber kann jemand über die perfect forwarding im obigen Fall der Ausnahmeerstellung erklären? Ist es richtig? Obwohl es kompiliert, ist es sinnvoll oder nützlich? Sollte die C++ - Bibliothek, die ich verwende, einen Move-Konstruktor implementiert haben, der für diese Art von Verwendung std::exception implementiert ist, um wirklich sinnvoll zu sein? Ich habe versucht, nach Artikeln und SO-Fragen zu rvalue-Referenzen in Bezug auf Ausnahmen zu suchen, konnte keine finden.

Antwort

7

Tatsächlich hat die Ausnahmebehandlung spezielle Regeln in Bezug auf Lvalues ​​und Rvalues. Das temporäre Ausnahmeobjekt ist ein L-Wert, 15,1/3 des aktuellen Entwurfs siehe:

ein Einwurf Ausdruck initialisiert ein temporäres Objekt, die so genannte Ausnahmeobjekt, die Art der, die durch Entfernen jeden Top-Level-cv bestimmt wird -Qualifier aus dem statischen Typ des Operanden von throw und die Anpassung der Art von "Array von T" oder "Funktion, die T" zu "Zeiger auf T" oder "Zeiger auf Funktion zurückgeben T". Das temporäre ist ein Lvalue und wird verwendet, um die Variable zu initialisieren, die in dem übereinstimmenden Handler (15.3) benannt wird. Wenn der Typ des Ausnahmeobjekts ein unvollständiger Typ oder ein Zeiger auf einen anderen unvollständigen Typ als (möglicherweise cv-qualifiziert) wäre, ist das Programm schlecht formatiert. Mit Ausnahme dieser Einschränkungen und der in 15.3 erwähnten Einschränkungen der Typübereinstimmung wird der Operand von throw genau wie ein Funktionsargument in einem Aufruf (5.2.2) oder der Operand einer return-Anweisung behandelt.

Und rvalue Referenzfang ist illegal, finden 15.3/1:

Die Ausnahme-Deklaration in einem Handler beschreibt die Art (en) von Ausnahmen, die die Handler eingegeben werden können dazu führen, . Die Ausnahme-Deklaration darf keinen unvollständigen Typ oder einen R-Wert-Referenztyp bezeichnen. Die Ausnahmeerklärung darf keinen Zeiger oder Verweis auf einen unvollständigen Typ bezeichnen, mit Ausnahme von void *, const void *, volatile void * oder const volatile void *.

Auch scheinen Sie nicht perfektes Weiterleiten zu verstehen. Dein Vorwärtsaufruf ist nicht besser als ein Zug. Die Idee der perfekten Weiterleitung besteht darin, die Wertkategorie des Arguments als Teil des Typs zu codieren und den Argumentargumentabzug für die Vorlage herauszufinden. Aber Ihr Ausnahmebehandler ist keine Funktionsschablone und kann auch keine Funktionsschablone sein.

Grundsätzlich perfekte Weiterleitung basiert auf Vorlage Argument Abzug und R-Wert-Referenzen:

void inner(const int&); // #1 takes only lvalues or const rvalues 
void inner(int&&);  // #2 takes non-const rvalues only 

template<class T> 
void outer(T && x) { 
    inner(forward<T>(x)); 
} 

int main() { 
    int k = 23; 
    outer(k); // outer<T=int&> --> forward<int&> --> #1 
    outer(k+2); // outer<T=int> --> forward<int> --> #2 
} 

In Abhängigkeit von der Wertkategorie des Arguments, Vorlage argumend Abzug folgert T entweder eine L-Wert Referenz oder ein normaler Wert Typ zu sein. Aufgrund des Referenzzusammenbruchs ist T & & auch ein lvalue Referenz im ersten Fall, oder ein rvalue Referenz im zweiten Fall. Wenn Sie sehen T & & und T ist ein Template-Parameter, der abgeleitet werden kann, ist es im Grunde ein "alles fangen".std :: forward stellt die ursprüngliche Wertkategorie wieder her (kodiert in T), so dass wir das Argument perfekt auf die überladenen inneren Funktionen weiterleiten und das richtige auswählen können. Aber das funktioniert nur, weil outer eine Vorlage ist und weil es spezielle Regeln gibt, um T in Bezug auf seine Wertkategorie zu bestimmen. Wenn Sie eine Rvalue-Referenz ohne Templates/Template-Argumentableitung (wie in # 2) verwenden, akzeptiert die Funktion nur rvalues.

+1

+1: Sie sagen, was ich gesagt habe, aber mehr und besser. Um nur den Hinweis explizit zu machen, wird eine einfache nicht-konstante Referenz übereinstimmen und in der Lage sein, das Ausnahmeobjekt zu modifizieren. – Potatoswatter

+0

@Potatoswatter: Entschuldigung für die Wiederholung der Hälfte Ihrer Antwort. Ich habe deinen Hinweis auf 15.1/3 wirklich vermisst, als ich deine Antwort überflogen habe. – sellibitze

+0

@sellibitze: Es macht keinen Sinn, sie gegenseitig auszuschließen. Sie können nichts ausleihen, solange Ihre Antwort richtig ist: v). – Potatoswatter