2016-08-12 1 views
15

Ich frage mich über ein C++ - Verhalten, wenn ein R-Wert zwischen den Funktionen übergeben wird.r-Wert Parameter in einer Funktion

Blick auf diese einfache Code:

#include <string> 

void foo(std::string&& str) { 
    // Accept a rvalue of str 
} 

void bar(std::string&& str) { 
    // foo(str);   // Does not compile. Compiler says cannot bind lvalue into rvalue. 
    foo(std::move(str)); // It feels like a re-casting into a r-value? 
} 

int main(int argc, char *argv[]) { 
    bar(std::string("c++_rvalue")); 
    return 0; 
} 

Ich weiß, wenn ich in bar Funktion bin ich move Funktion verwenden, um benötigen foo Funktion aufzurufen. Meine Frage ist jetzt warum?

Wenn ich in der bar Funktion bin die Variable str sollte schon sein, ein r-Wert, aber der Compiler fungiert, wie es ein l-Wert ist.

Kann jemand einen Verweis auf den Standard zu diesem Verhalten zitieren? Danke!

+5

Benanntes Objekt ist immer Lvalue. Deshalb brauchst du "bewegen". –

Antwort

10

str ist ein rvalue Referenz, d.h. es ist eine Referenz nur auf rvalues ​​. Aber es ist immer noch eine Referenz, die ein Wert ist. Sie können str als Variable verwenden, was auch bedeutet, dass es sich um einen Lvalue und nicht um einen temporären rvalue handelt.

Ein lvalue ist nach §3.10.1.1:

Ein lvalue (so genannt, historisch gesehen, weil lvalues ​​auf der linken Seite einer Zuweisung Ausdruck erscheinen könnte) a bezeichnet Funktion oder ein Objekt.[Beispiel: Wenn E ein Ausdruck des Zeigertypen ist, dann * E ist eine L-Wert-Expression auf das Objekt verweisen oder Funktion zu dem E Punkten. Als weiteres Beispiel ist das Ergebnis des Aufrufs einer Funktion, deren Rückgabetyp eine L-Wert-Referenz ist, ein L-Wert. - Ende Beispiel]

Und ein rvalue ist nach §3.10.1.4:

Ein rvalue (so genannt, historisch, weil rvalues ​​auf der rechten erscheinen könnte Handseite einer Zuweisung Ausdruck) ist ein xvalue, ein temporäres Objekt (12.2) oder ein Unterobjekt davon, oder ein Wert, der keinem Objekt zugeordnet ist.

Auf dieser Basis ist str keine temporäre Objekt, und es ist einem Objekt zugeordnet (mit dem Objekt str genannt), und so ist es kein R-Wert.

Das Beispiel für den Lvalue verwendet einen Zeiger, aber es ist das gleiche für Referenzen und natürlich für Rvalue-Referenzen (die nur eine spezielle Art von Referenzen sind).

Also, in Ihrem Beispiel strist ein lvalue, so dass Sie std::move es haben foo zu nennen (die nur rvalues ​​akzeptiert, nicht lvalues).

7

Der "R-Wert" in "rvalue reference" bezieht sich auf die Art von Wert, dass die Referenz kann binden an:

  • lvalue Verweise auf lvalues ​​
  • rvalue Verweise auf rvalues ​​binden können
  • binden können
  • (+ ein bisschen mehr)

das ist alles gibt es zu. Wichtig ist, dass es nicht bezieht sich auf den Wert, der erhalten, wenn Sie die Referenz verwenden. Sobald Sie eine Referenzvariable (irgendeine Art von Referenz!) Haben, ist der ID-Ausdruck, der diese Variable benennt, immer ein Lvalue. Rvalues ​​treten nur als temporäre Werte oder als Werte von Funktionsaufrufausdrücken oder als der Wert eines Darstellungsausdrucks oder als das Ergebnis des Zerfalls oder von this in der freien Wildbahn auf.

Es gibt eine gewisse Analogie mit dereferencing einen Zeiger: einen Zeiger Dereferenzierung ist immer ein L-Wert, egal wie der Zeiger erhalten wurde: *p, *(p + 1), *f() alle lvalues ​​sind. Es ist egal, wie du zu dem Ding gekommen bist; Sobald Sie es haben, ist es ein Wert.

Ein Schritt zurück, vielleicht der interessanteste Aspekt von all dem ist, dass rvalue-Referenzen ein Mechanismus sind, um einen rvalue in einen lvalue zu konvertieren. Es gab keinen solchen Mechanismus vor C++ 11, der veränderbare Werte erzeugte. Während die Konvertierung von lvalue-to-rvalue seit den Anfängen Teil der Sprache war, dauerte es viel länger, bis die Notwendigkeit der rvalue-to-lvalue-Konvertierung erkannt wurde.

3

Meine Frage ist jetzt warum?

Ich füge eine weitere Antwort hinzu, weil ich eine Antwort auf das "Warum" betonen möchte.

Auch wenn mit dem Namen rvalue-Referenzen an einen rvalue binden können, werden sie bei der Verwendung als lvalues ​​behandelt. Zum Beispiel:

struct A {}; 

void h(const A&); 
void h(A&&); 

void g(const A&); 
void g(A&&); 

void f(A&& a) 
{ 
    g(a); // calls g(const A&) 
    h(a); // calls h(const A&) 
} 

Obwohl ein R-Wert auf die a Parameter von f() binden kann, einmal gebunden, a wird nun als L-Wert behandelt. Insbesondere werden Aufrufe an die überladenen Funktionen g() und h() an die const A& (lvalue) Überlast aufgelöst. Behandlung a als R-Wert innerhalb f zu fehleranfälligen Code führen würde: Zuerst wird die „move-Version“ von g() würde genannt werden, die wahrscheinlich a stibitzen würde, und dann würde der pilfered a den Umzug Überlastung von h() gesendet werden.

Reference.

Verwandte Themen