
Jonathan Mei
Ich habe diese Antwort gepostet: https://stackoverflow.com/a/28459180/2642059, die den folgenden Code enthält:
void foo(string&& bar){
string* temp = &bar;
cout << *temp << " @:" << temp << endl;
}
Ist bar
ein rvalue oder ein lvalue?
Ich frage, weil ich offensichtlich nicht die Adresse eines rvalue nehmen kann, aber ich kann die Adresse einer rvalue-Referenz nehmen, wie es hier gemacht wird.
Wenn Sie jede Operation für eine rvalue-Referenz ausführen können, die Sie für eine lvalue-Referenz ausführen können, was ist der Punkt, wenn Sie zwischen den beiden mit dem “&&” anstelle von nur einem “&” unterscheiden?

Angew ist nicht mehr stolz auf SO
Ist bar
ein rvalue oder ein lvalue?
Die Frage beantwortet sich von selbst. Was auch immer einen Namen hat, ist ein Wert(1). Damit bar
ist ein lvalue. Es ist Art ist “rvalue Referenz auf string
“, aber es ist ein lvalue dieses Typs.
Wenn Sie es als Rvalue behandeln möchten, müssen Sie es anwenden std::move()
dazu.
Wenn Sie jede Operation für eine rvalue-Referenz ausführen können, die Sie für eine lvalue-Referenz ausführen können, was ist der Punkt, wenn Sie zwischen den beiden mit dem “&&” anstelle von nur einem “&” unterscheiden?
Dies hängt von Ihrer Definition von „eine Operation ausführen“ ab. Eine lvalue-Referenz und eine (benannte) rvalue-Referenz sind ziemlich identisch in der Art und Weise, wie Sie sie in Ausdrücken verwenden können, aber sie unterscheiden sich viel in dem, was sie binden kann. Lvalues können an Lvalue-Referenzen gebunden werden, Rvalues können an Rvalue-Referenzen gebunden werden (und alles kann an eine Lvalue-Referenz gebunden werden const
). Das heißt, Sie können einen Rvalue nicht an eine Lvalue-Referenz binden oder umgekehrt.
Lassen Sie uns über einen Funktionsparameter des Rvalue-Referenztyps sprechen (z. B. Ihre bar
). Der wichtige Punkt ist nicht was bar
ist, aber was wissen Sie über den Wert zu dem bar
verweist. Seit bar
eine rvalue-Referenz ist, wissen Sie mit Sicherheit, was auch immer daran gebunden ist war ein rvalue. Was bedeutet, dass es zerstört werden muss, wenn der vollständige Ausdruck endet, und Sie es sicher als Rvalue behandeln können (indem Sie seine Ressourcen stehlen usw.).
Wenn Sie nicht derjenige sind, der dies direkt tut bar
aber nur passieren wollen bar
on haben Sie zwei Möglichkeiten: Entweder Sie sind fertig bar
und dann sollten Sie dem nächsten, der es erhält, mitteilen, dass es an einen R-Wert gebunden ist – tun Sie es std::move(bar)
. Oder Sie müssen noch einige Dinge mit tun bar
und Sie möchten nicht, dass irgendjemand zwischendurch seine Ressourcen unter Ihnen stiehlt, also behandeln Sie es einfach als Lvalue—bar
.
Um es zusammenzufassen: Der Unterschied besteht nicht darin, was Sie mit der Referenz tun können sobald du es hast. Der Unterschied ist was binden kann die Referenz.
(1) Eine gute Faustregel mit kleinen Ausnahmen: Enumeratoren haben Namen, sind aber Rvalues. Klassen, Namensräume und Klassenvorlagen haben Namen, sind aber keine Werte.
Ist bar an rwert oder ein Wert?
Es ist ein Wertwie jeder Ausdruck, der eine Variable benennt.
Wenn Sie eine Operation an einem durchführen können rwert Referenz, die Sie auf ein können Wert Referenz Was bringt es, zwischen den beiden mit dem “&&” statt nur einem “&” zu unterscheiden?
Sie können nur eine initialisieren rwert Referenz mit an rwert Ausdruck. Sie können also einen temporären String an Ihre Funktion übergeben, aber Sie können keine String-Variable übergeben, ohne explizit von ihr wegzugehen. Dies bedeutet, dass Ihre Funktion davon ausgehen kann, dass das Argument nicht mehr erwünscht ist, 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

Sam Wong
Der Ausdruck bar
ist ein lvalue. Aber es ist kein “rvalue-Verweis auf String”. Der Ausdruck bar
ist eine Lvalue-Referenz. Sie können dies überprüfen, indem Sie die folgende Zeile in foo() hinzufügen:
cout << is_lvalue_reference<decltype((bar))>::value << endl; // prints "1"
Aber ich stimme dem Rest der Erklärung von Angew zu.
Eine Rvalue-Referenz ist, nachdem sie an einen Rvalue gebunden wurde, eine Lvalue-Referenz. Eigentlich ist es nicht nur für Funktionsparameter:
string&& a = "some string";
string&& b = a; // ERROR: it does not compile because a is not an rvalue
Wenn Sie jede Operation für eine rvalue-Referenz ausführen können, die Sie für eine lvalue-Referenz ausführen können, was ist der Punkt, wenn Sie zwischen den beiden mit dem “&&” anstelle von nur einem “&” unterscheiden?
Eine Rvalue-Referenz ermöglicht es uns, einige “Lvalue-Operationen” durchzuführen, bevor der Rvalue abläuft.
Hier in diesem Fall benannte Rvalue-Referenzen sind Lvalueswährend wenn der Eingabetyp ist const benannte Rvalue-Referenzen dann wird es sein rwerthier 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.
9901900cookie-checkRvalue-Referenz wird als Lvalue behandelt?yes
Der ganze Sinn von rvalues besteht darin, Bewegungssemantik zu ermöglichen, sie sind in den meisten Fällen (außer ihrem Typ) nur Referenzen.
– Guvante
12. Februar 2015 um 17:01 Uhr
In Ihrem Beispiel bewegen Sie sich nicht zweimal, Sie kopieren zweimal, als
bar
ist ein lvalue (sein Typ ist ein rvalue, komisch ich weiß). Wenn Sie angerufen habenstd::move
zweimal könnten Sie jedoch auf Probleme stoßen.– Guvante
12. Februar 2015 um 17:20 Uhr
@JonathanMee Und diese doppelte Verwendung von
bar
ist genau warumbar
ist ein lvalue – damit Ihnen eine solche doppelte Verwendung keine Probleme bereitet. Es wird nur dann als Rvalue behandelt, wenn Sie es explizit tun:std::move(bar)
.– Angew ist nicht mehr stolz auf SO
12. Februar 2015 um 17:23 Uhr
@JonathanMee: Du denkst auf der falschen Seite des Zauns. Eine Rvalue-Referenz wird verschoben hinein nicht bewegt aus. Das wissen Sie, wenn Ihre Funktion aufgerufen wird
bar
wird zerstört, das ist, was Sie bekommen. Wenn Sie dann jedoch eine Funktion in Ihrer aufrufen, geben Sie diese Garantie nicht ab, es sei denn, Sie sagen es ausdrücklich.– Guvante
12. Februar 2015 um 17:27 Uhr
“Warum nennen wir es dann überhaupt eine Rvalue-Referenz?” – weil es sich um einen Verweis auf einen Rvalue handelt (oder pedantisch auf ein Objekt, das durch einen Rvalue-Ausdruck bezeichnet wird). Das heißt, es bezieht sich auf eine temporäre oder eine Variable, aus der explizit verschoben wird (using
std::move
oder Äquivalent), sodass Ihre Funktion davon ausgehen kann, dass es in Ordnung ist, sich von ihr zu bewegen.– Mike Seymour
12. Februar 2015 um 17:34 Uhr