2015-02-12 12 views
15

Ich stellte diese Antwort: https://stackoverflow.com/a/28459180/2642059, die den folgenden Code enthält:Rvalue-Referenz wird als L-Wert behandelt?

void foo(string&& bar){ 
    string* temp = &bar; 

    cout << *temp << " @:" << temp << endl; 
} 

Ist bar ein R-Wert oder ein L-Wert?

Ich frage, weil ich natürlich nicht die Adresse eines rvalue nehmen kann, aber ich kann die Adresse eines rvalue Bezug zu nehmen, wie hier geschehen ist.

Wenn Sie irgendeine Operation an einer rvalue Referenz durchführen können, dass man auf einer L-Wert-Referenz, was der Punkt zwischen den beide mit dem „& &“ statt nur eine „&“ bei der Differenzierung ist?

+1

Der gesamte Punkt von rvalues ​​ist, Bewegungssemantik zu erlauben, sie sind in den meisten Arten (außer ihrem Typ) gerade Verweisungen. – Guvante

+0

@Guvante [MikeSeymour] (http://StackOverflow.com/users/204847/mike-seymour) 's Antwort half mir, das zu verstehen. Aber wie bei dem Kommentar, den ich ihm geschrieben habe, würde das nicht zu Problemen führen, wenn wir es zweimal konstruieren würden? Sprich: 'string temp1 (bar), temp2 (bar);' –

+2

In deinem Beispiel bewegst du dich nicht zweimal, du kopierst zweimal, da 'bar' ein lvalue ist (sein Typ ist ein rvalue, seltsam, weiß ich). Wenn Sie 'std :: move' zweimal aufgerufen haben, könnten Sie jedoch Probleme bekommen. – Guvante

Antwort

23

Ist bar ein R-Wert oder ein L-Wert?

Die Frage beantwortet sich von selbst. Was auch immer einen Namen hat, ist ein Wert (1). Also bar ist ein Lvalue. Sein Typ ist "rvalue Verweis auf string", aber es ist ein Lvalue dieses Typs.

Wenn Sie es als ein R-Wert behandeln wollen, müssen Sie std::move(), um es anzuwenden.


Wenn Sie irgendeine Operation an einer rvalue Referenz durchführen können, dass man auf einer L-Wert-Referenz, was der Punkt zwischen den beide mit dem „& &“ statt nur eine „&“ bei der Differenzierung ist?

Dies hängt von Ihrer Definition von "Ausführen einer Operation" ab. Ein L-Wert-Referenz und ein (benannt) rvalue Referenz sind ziemlich identisch, wie man sie in Ausdrücken verwenden können, aber sie unterscheiden sich viel in dem, was an sie binden können. Lvalues ​​können an lvalue-Referenzen binden, rvalues ​​können an rvalue-Referenzen binden (und alles kann an einen lvalue-Verweis an const gebunden werden). Das heißt, Sie können keinen Rvalue an eine Lvalue-Referenz binden oder umgekehrt.

Reden wir über einen Funktionsparameter von rvalue Referenztyp (wie Ihre bar). Der wichtige Punkt ist nicht, was bar ist, sondern was Sie über den Wert wissen, auf den sich bar bezieht. Da bar eine Rvalue-Referenz ist, wissen Sie sicher, dass, was auch immer daran gebunden ein Rvalue ist. Das bedeutet, dass es zerstört werden muss, wenn der vollständige Ausdruck endet, und Sie können es sicher als einen Rvalue behandeln (indem Sie seine Ressourcen usw. stehlen).

Wenn Sie nicht derjenige, dies zu bar direkt zu tun, sondern wollen nur bar weitergeben müssen, haben Sie zwei Möglichkeiten: Entweder Sie mit bar getan, und dann sollten Sie das nächste sagen, wer es empfängt, es ist an einen rvalue — do std::move(bar) gebunden. Oder Sie müssen mehr Dinge mit bar zu tun, und so wollen Sie nicht, dass jemand zwischendurch seine Ressourcen unter Ihnen zu stehlen, so behandeln sie nur als L-Wert — bar.

Um es zusammenzufassen: Der Unterschied ist nicht in dem, was Sie mit der Referenz tun können, sobald Sie es haben. Der Unterschied ist, was an die Referenz binden kann.


(1) Eine gute Faustregel mit kleinen Ausnahmen: Aufzählungen Namen haben, aber sind rvalues. Klassen, Namespaces und Klassenvorlagen haben Namen, sind aber keine Werte.

+0

@Agnew Was würde den zweiten Teil meiner Frage auslösen, was ist der Sinn es dann eine Rvalue Referenz zu nennen? Warum nicht einfach rvalue- und lvalue-Referenzen aufrufen: Referenzen? –

+0

@ JonathanMee Versucht zu erklären. Bitte lassen Sie es mich wissen, wenn es klarer ist oder mehr braucht. – Angew

+1

@ JonathanMee es ist ein Verweis auf etwas, das sicher als Rvalue behandelt werden kann (das heißt, Sie können 'std :: move' darauf aufrufen, und Sie riskieren nicht, ein Objekt zu entwerten, das anderswo erwartet werden könnte, um gültig zu bleiben). – jalf

8

ist bar ein rvalue oder eine lvalue?

Es ist ein lvalue, wie jeder Ausdruck ist eine Variable, die Namen.

Wenn Sie auf eine rvalue Referenz eine Operation durchführen können, dass man auf eine lvalue Referenz, was der Punkt zwischen den beide mit dem „& &“ statt nur eine „&“ bei der Differenzierung ist?

können Sie nur eine rvalue Referenz mit einem rvalue Ausdruck initialisieren. Sie können also eine temporäre Zeichenfolge an Ihre Funktion übergeben, aber Sie können keine Zeichenfolgenvariable übergeben, ohne explizit von dieser zu wechseln. Dies bedeutet, dass Ihre Funktion davon ausgehen kann, dass das Argument nicht mehr benötigt wird, und sich davon entfernen kann.

std::string variable; 
foo(variable);   // ERROR, can't pass an lvalue 
foo(std::move(variable)); // OK, can explicitly move 
foo("Hello");    // OK, can move from a temporary 
+0

Sie sagen also, der Punkt einer Rvalue-Referenz soll dem Compiler mitteilen, dass er, wenn er diesen Wert in einem Konstruktor verwendet, den Move-Konstruktor verwenden kann? Würde dies nicht zu Problemen führen, wenn ich zweimal initialisiere? Say 'string temp1 (bar), temp2 (bar);' –

+2

@ JonathanMee: Nein, der Punkt einer rvalue-Referenz ist, dass sie nur an einen rvalue binden kann. In diesem Beispiel ist "bar" ein Lvalue, also werden beide 'temp' Variablen mit dem Copy Konstruktor initialisiert (unter Verwendung eines lvalue Verweises), nicht der Move Konstruktor (unter Verwendung eines rvalue Verweises) - verhindert Probleme, die verursacht werden könnten, wenn Der erste Konstruktor wurde von "bar" verschoben. –

1

Hier in diesem Fall genannt rvalue Referenzen sind lvalues ​​, während, wenn der Eingangstyp ist konst namens rvalue verweist dann wird es rvalue, hier ist ein einfaches Beispiel :

#include <string> 
#include <iostream> 

void foo(const std::string&& bar) { 
    std::string* temp = &bar; // compile error, can't get address 
    std::cout << *temp << " @:" << temp << std::endl; 
} 

int main() 
{ 
    foo("test"); 
    return 0; 
} 

Hoffe, das ist nützlich.

+0

Das scheint also zu stimmen. Können Sie dazu eine Quelle angeben? –

+0

https://codesynthesis.com/~boris/blog//2012/07/24/const-rvalue-references/,dieser Link ist nützlich –