Rvalue-Referenz wird als Lvalue behandelt?

Lesezeit: 8 Minuten

Rvalue Referenz wird als Lvalue behandelt
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?

  • 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 haben std::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 warum bar 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

1646983211 572 Rvalue Referenz wird als Lvalue behandelt
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 baraber nur passieren wollen bar on haben Sie zwei Möglichkeiten: Entweder Sie sind fertig barund 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 barund 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.

  • @Agnew Was den zweiten Teil meiner Frage auslösen würde, was bringt es dann, es als Rvalue-Referenz zu bezeichnen? Warum rufen Sie nicht einfach rvalue- und lvalue-Referenzen auf: Referenzen?

    – Jonathan Mei

    12. Februar 2015 um 17:01 Uhr


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

    – Angew ist nicht mehr stolz auf SO

    12. Februar 2015 um 17:17 Uhr

  • @JonathanMee es ist ein Verweis auf etwas, das sicher als rvalue behandelt werden kann (d. h. Sie können aufrufen std::move darauf, und Sie riskieren nicht, ein Objekt ungültig zu machen, von dem möglicherweise anderswo erwartet wird, dass es gültig bleibt).

    – jalf

    12. Februar 2015 um 17:25 Uhr

  • @JonathanMee Nein, es ist nicht nur ein Hinweis für den Programmierer. Es ist ein harte Regel auf der Außenseite. 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.

    – Angew ist nicht mehr stolz auf SO

    12. Februar 2015 um 17:31 Uhr

  • “Was einen Namen hat, ist ein Wert.” Umständliche Bemerkung: Enumeratoren sind rvalues. Gibt es eine einfache Regel, die das in Einklang bringt? (zB “Namen von Variablen und Funktionen sind lvalues”)

    – dyp

    12. Februar 2015 um 18:08 Uhr

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

  • Sie sagen also, der Zweck einer rvalue-Referenz besteht darin, den Compiler darüber zu informieren, dass er den Move-Konstruktor verwenden kann, wenn er diesen Wert in einem Konstruktor verwendet? Würde dies nicht zu Problemen führen, wenn ich zweimal initialisiere? Sagen string temp1(bar), temp2(bar);

    – Jonathan Mei

    12. Februar 2015 um 17:05 Uhr

  • @JonathanMee: Nein, der Punkt einer Rvalue-Referenz ist, dass sie nur an einen Rvalue gebunden werden kann. In diesem Beispiel bar ist ein lvalue, also beides temp Variablen werden mit dem Kopierkonstruktor (unter Verwendung einer lvalue-Referenz) initialisiert, nicht mit dem Bewegungskonstruktor (unter Verwendung einer rvalue-Referenz) – wodurch Probleme vermieden werden, die verursacht werden könnten, wenn der erste Konstruktor von verschoben wird bar.

    – Mike Seymour

    12. Februar 2015 um 17:30 Uhr

1646983212 890 Rvalue Referenz wird als Lvalue behandelt
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.

  • Um es klar zu sagen: “Die Art der Bar” zu sagen, ist so wie es ist mehrdeutig. Das Token bar könnte syntaktisch entweder ein Ausdruck oder kein Ausdruck sein. Wenn es sich syntaktisch um einen Ausdruck handelt, dann ist der Typ std::string (keine Referenz). Wenn es jedoch syntaktisch der Operand von ist decltype(id-expression) dann ist der Typ std::string&&.

    – MM

    5. Juli 2018 um 5:06 Uhr


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.

990190cookie-checkRvalue-Referenz wird als Lvalue behandelt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy