2016-12-01 3 views
1

Diese sehr einfache Code ist erlaubt:Warum beide const/nonconst lvalue-Referenzen an eine rvalue-Referenz binden?

class s 
{ 
public: 
}; 

const s& tt = std::move(s()); // Also valid without 'const' 

Aber jetzt ich frage mich, warum es erlaubt ist ..

Zuerst haben wir std::move verwenden und einen R-Wert markieren (die temporäre) als rvalue Referenz , aber warum eine lvalue-Referenz an eine rvalue-Referenz binden kann?

Liegt es daran, dass ein R-Wert auch ein R-Wert ist?

Was ist die Grundregel (oder Standard-Anführungszeichen), die eine Lvalue-Referenz verursacht, um an eine Rvalue-Referenz zu binden? Das es kann getan werden Ich habe es.

Edit: msvc2015 ermöglicht nichtconst lvalue Referenzen an rvalue Referenzen zu binden, die Frage bleibt für const lvalue Referenzen Bindung an Rvalue Referenzen. Entschuldigung, ich hätte den Compiler, den ich verwendet habe, angegeben.

+0

Ich finde diese Frage ein wenig schwer zu parsen, weil Sie die Terminologie falsch herum verwenden: In 'T & t = u;', sagen wir, dass "(die Referenz)" t 'an den Wert 'u bindet "." Beachten Sie außerdem, dass Referenzen immer an * Werte * gebunden sind; Eine Referenz bindet nicht an eine andere Referenz. Referenztypen sind immer mit * Variablen * (oder Funktionen) verbunden, niemals mit Ausdrücken. In der üblichen Verwendung der Terminologie könnte die Frage lauten: "Warum binden sich lvalue-Referenzen an rvalues?", Was eine gute Frage ist (mit einer einfachen Antwort). –

+0

@KerrekSB Meine schlechte, ich habe die Frage bearbeitet, um (hoffentlich) die richtige Terminologie zu verwenden – Dean

+4

es [kann nicht kompilieren] (http://coliru.stacked-crooked.com/a/3aca61d34a714765) ohne 'const' –

Antwort

4

Rvalue Referenzen werden implizit rvalues ​​(genauer gesagt zu XValues) umgewandelt, wie eine der Standardumwandlungen (Kapitel 4 des C++ Standard):

Die Wirkung jeder impliziten Umwandlung ist das gleiche wie die Durchführung die entsprechende Deklaration und Initialisierung und dann die temporäre Variable als Ergebnis der Konvertierung. Das Ergebnis ist ein lvalue wenn T eine L-Wert oder eine Referenztyp rvalue Bezugnahme auf Funktionstyp (8.3.2) ist, ein xValue wenn T eine rvalue Referenz Typen und ein prvalue sonst

einzuwenden

Rvalues ​​(einschließlich XValues) an const lvalue Referenzen gebunden werden, so dass man eine temporäre auf eine Funktion mit einem solchen Parameter übergeben werden kann:

void foo(const bar &a); 
// ... 
foo(bar()); 

(A vorübergehend ist ein R-Wert, in diesem Fall t Das Ergebnis von bar() ist ein rvalue). Es gibt keinen Grund nicht dies zu erlauben, da das temporäre immer so lange lebt wie der enthaltene Ausdruck - in diesem Fall der Funktionsaufruf - und so wird es keinen baumelnden Verweis innerhalb foo erstellen.

Das bedeutet, dass es immer möglich ist, die Signatur einer Funktion fun(bar) an fun(const bar &) anzupassen - und natürlich die Implementierung entsprechend zu ändern! - da temporäre Argumente immer noch akzeptiert werden und die Semantik aus Sicht des Aufrufers gleich sein sollte; const bedeutet, dass das Objekt nicht geändert wird, was auch der Fall ist, wenn es durch Kopieren übergeben wird.

Nicht-const Referenzen sind nicht erlaubt; Ein praktischer Grund ist, weil sie implizieren, dass der Wert in einer sinnvollen Weise geändert werden sollte, und wenn der Wert ein vorübergehender Wert ist, würde dies verloren gehen. Sie können jedoch einen R-Wert in einen L-Wert konvertieren, wenn Sie dies wirklich wollen, mit einigen Einschränkungen, wie in this answer zu einer anderen Frage beschrieben.

Die Möglichkeit, einen Rvalue an eine const lvalue-Referenz zu binden, abgesehen von der temporären Übergabe von Argumenten, eignet sich auch für Fälle, in denen der genaue Parametertyp nicht bekannt ist, Sie aber Bewegungssemantik zulassen möchten . Nehmen wir an, dass ich eine Funktion nenne, die als foo2(const bar &) oder foo2(bar) und die im ersten Fall definiert werden kann oder nicht eine Überlastung kann foo2(bar &&), und ich möchte, damit bewegen Semantik zu verwenden, wenn möglich (unter der Annahme, dass eine foo2(bar &&) Überlastung verwenden Verschieben Sie die Semantik in ihrer Implementierung); Ich kann sicher std::move verwenden, um einen Rvalue zu erstellen, da es in jedem Fall gilt. Dieses Beispiel mag ein wenig künstlich wirken, aber es ist eine Sache, die beim Schreiben von Templates ziemlich oft auftauchen kann. In Code:

bar bb = bar(); 
foo2(std::move(bb)); 
// above is legal if foo2 is declared as either: 
// foo2(bar) 
// foo2(const bar &) 
// and in latter case calls overload foo2(bar &&) if it is defined. 

Im Fall von anderen rvalue-zu-L-Wert-Referenz Zuweisungen eines vorübergehende Beteiligung wird die Lebensdauer der temporären derjenigen der Referenz erweitert, so dass sie baumelnden Referenzen nicht einmal in Kontexten erstellt andere als Parameterübergabe:

const bar &b = bar(); // temporary lifetime is extended 

In dem oben die bar Objekt nicht zerstört werden, bis b die Referenz den Gültigkeitsbereich verlässt.

+0

Dies ist eine gute Antwort, aber ich fühle es vermisst den Punkt meiner Frage: ** Was ist die Begründung (oder Standard-Anführungszeichen), die eine Lvalue-Referenz an eine Rvalue-Referenz binden? ** Das * es kann getan werden * Ich habe es. – Dean

+0

@Dean ist es so, dass Sie eine temporäre zu einer Funktion übergeben können, die eine 'const' Lvalue-Referenz akzeptiert, so dass es wiederum keinen semantischen Unterschied aus der Sicht des Aufrufers zwischen' foo (const bar &) 'und' foo (bar) '. Es ist alles in der Antwort. – davmac

+0

@Dean und auch: "Es gibt keinen Grund * nicht *, dies zuzulassen" ... also warum denkst du, dass ein rvalue * nicht in der Lage sein sollte, an eine Lvalue-Referenz zu binden? – davmac

Verwandte Themen