Es deklariert eine Wertreferenz (Standardvorschlagsdokument).
Hier ist eine Einführung in rvalue- Referenzen .
Hier ist ein fantastischer in tiefem Einblick in rvalue Referenzen von einer Microsoft-Standard - Bibliothek Entwicklern .
VORSICHT: Der verlinkte Artikel zu MSDN ("R-Wert-Referenzen: C ++ 0x-Funktionen in VC10, Teil 2") ist eine sehr klare Einführung in R-Wert-Referenzen, enthält jedoch Aussagen zu R-Wert-Referenzen, die im Entwurf von C ++ 11 einmal zutrafen Standard, aber nicht wahr für den letzten! Insbesondere heißt es an verschiedenen Stellen, dass r-Wert-Referenzen an l-Werte binden können, was einmal wahr war, aber geändert wurde (z. B. int x; int & rrx = x; wird nicht mehr in GCC kompiliert) - drawbarbs 13. Juli 14 um 16:12
Der größte Unterschied zwischen einer C ++ 03-Referenz (in C ++ 11 jetzt als lvalue-Referenz bezeichnet) besteht darin, dass sie wie eine temporäre an einen rvalue gebunden werden kann, ohne const sein zu müssen. Somit ist diese Syntax jetzt legal:
T&& r = T();
rWertreferenzen sehen in erster Linie Folgendes vor:
Semantik verschieben . Es kann jetzt ein Verschiebungskonstruktor und ein Verschiebungszuweisungsoperator definiert werden, die anstelle der üblichen const-l-Wertreferenz eine r-Wert-Referenz verwenden. Ein Umzug funktioniert wie eine Kopie, außer dass er nicht verpflichtet ist, die Quelle unverändert zu lassen. Tatsächlich ändert es normalerweise die Quelle so, dass es die verschobenen Ressourcen nicht mehr besitzt. Dies ist ideal, um überflüssige Kopien zu entfernen, insbesondere bei Standardbibliotheksimplementierungen.
Ein Kopierkonstruktor könnte beispielsweise folgendermaßen aussehen:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Wenn dieser Konstruktor als temporär übergeben würde, wäre die Kopie nicht erforderlich, da wir wissen, dass das temporäre Element nur zerstört wird. Warum nicht die Ressourcen nutzen, die die temporäre bereits zugewiesen hat? In C ++ 03 gibt es keine Möglichkeit, die Kopie zu verhindern, da wir nicht feststellen können, dass uns eine temporäre Kopie übergeben wurde. In C ++ 11 können wir einen Verschiebungskonstruktor überladen:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Beachten Sie hier den großen Unterschied: Der Verschiebungskonstruktor ändert tatsächlich sein Argument. Dies würde das Temporäre effektiv in das zu konstruierende Objekt "verschieben", wodurch die unnötige Kopie eliminiert würde.
Der Verschiebungskonstruktor wird für temporäre und nicht konstante Wertreferenzen verwendet, die mithilfe der std::move
Funktion explizit in Wertreferenzen konvertiert werden (er führt nur die Konvertierung durch). Der folgende Code ruft den Verschiebungskonstruktor für f1
und auf f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Perfekte Weiterleitung . Mit rvalue-Referenzen können wir Argumente für Vorlagenfunktionen ordnungsgemäß weiterleiten. Nehmen Sie zum Beispiel diese Werksfunktion:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Wenn wir aufrufen factory<foo>(5)
, wird das Argument abgeleitet int&
, das nicht an ein Literal 5 gebunden ist, selbst wenn foo
der Konstruktor ein nimmt int
. Nun, wir könnten stattdessen verwenden A1 const&
, aber was ist, wenn foo
das Konstruktorargument als nicht konstante Referenz verwendet wird? Um eine wirklich allgemeine Fabrik Funktion zu machen, müssten wir , um eine Überlastung Fabrik auf A1&
und auf A1 const&
. Das mag in Ordnung sein, wenn werkseitig 1 Parametertyp verwendet wird, aber jeder zusätzliche Parametertyp würde die erforderliche Überlast mit 2 multiplizieren. Das ist sehr schnell nicht zu warten.
rvalue-Referenzen beheben dieses Problem, indem die Standardbibliothek eine std::forward
Funktion definieren kann, mit der lvalue / rvalue-Referenzen ordnungsgemäß weitergeleitet werden können. Weitere Informationen zur Funktionsweise std::forward
finden Sie in dieser hervorragenden Antwort .
Dies ermöglicht es uns, die Factory-Funktion folgendermaßen zu definieren:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Jetzt bleibt der Wert / Wert des Arguments erhalten, wenn er an den T
Konstruktor übergeben wird. Das heißt, wenn die Factory mit einem r-Wert aufgerufen wird, wird T
der Konstruktor mit einem r-Wert aufgerufen. Wenn factory mit einem lvalue aufgerufen wird, wird T
der Konstruktor mit einem lvalue aufgerufen. Die verbesserte Werksfunktion funktioniert aufgrund einer speziellen Regel:
Wenn der Funktionsparametertyp die Form hat, T&&
in der T
es sich um einen Vorlagenparameter handelt, und das Funktionsargument ein Wert vom Typ ist A
, wird der Typ A&
für die Ableitung von Vorlagenargumenten verwendet.
So können wir Fabrik wie folgt verwenden:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Wichtige rWertreferenz-Eigenschaften :
- Für die Überlastungsauflösung bevorzugen l-Werte die Bindung an l-Wert-Referenzen und r-Werte die Bindung an r-Wert-Referenzen . Daher bevorzugen Temporäre den Aufruf eines Verschiebungskonstruktors / Verschiebungszuweisungsoperators gegenüber einem Kopierkonstruktor / Zuweisungsoperator.
- rWertreferenzen werden implizit an rWerte und an temporäre Werte gebunden, die das Ergebnis einer impliziten Konvertierung sind . dh
float f = 0f; int&& i = f;
ist gut geformt, weil float implizit in int konvertierbar ist; Der Verweis würde sich auf ein temporäres Element beziehen, das das Ergebnis der Konvertierung ist.
- Benannte rWertreferenzen sind lWerte. Unbenannte rWertreferenzen sind rWerte. Dies ist wichtig, um zu verstehen, warum der
std::move
Anruf erforderlich ist in:foo&& r = foo(); foo f = std::move(r);