Ich habe mir einige der neuen Funktionen von C ++ 11 angesehen und eine, die mir aufgefallen ist, ist das doppelte kaufmännische Und beim Deklarieren von Variablen, wie z T&& var
.
Wie heißt dieses Biest? Ich wünschte, Google würde uns erlauben, nach solchen Interpunktionen zu suchen.
Was genau bedeutet das?
Auf den ersten Blick scheint es sich um eine Doppelreferenz zu handeln (wie bei den Doppelzeigern im C-Stil T** var
), aber es fällt mir schwer, einen Anwendungsfall dafür zu finden.
c++
c++11
rvalue-reference
c++-faq
perfect-forwarding
paxdiablo
quelle
quelle
:)
Antworten:
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 .
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:
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:
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:
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ürf1
und auff2
:Perfekte Weiterleitung . Mit rvalue-Referenzen können wir Argumente für Vorlagenfunktionen ordnungsgemäß weiterleiten. Nehmen Sie zum Beispiel diese Werksfunktion:
Wenn wir aufrufen
factory<foo>(5)
, wird das Argument abgeleitetint&
, das nicht an ein Literal 5 gebunden ist, selbst wennfoo
der Konstruktor ein nimmtint
. Nun, wir könnten stattdessen verwendenA1 const&
, aber was ist, wennfoo
das Konstruktorargument als nicht konstante Referenz verwendet wird? Um eine wirklich allgemeine Fabrik Funktion zu machen, müssten wir , um eine Überlastung Fabrik aufA1&
und aufA1 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 Funktionsweisestd::forward
finden Sie in dieser hervorragenden Antwort .Dies ermöglicht es uns, die Factory-Funktion folgendermaßen zu definieren:
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, wirdT
der Konstruktor mit einem r-Wert aufgerufen. Wenn factory mit einem lvalue aufgerufen wird, wirdT
der Konstruktor mit einem lvalue aufgerufen. Die verbesserte Werksfunktion funktioniert aufgrund einer speziellen Regel:So können wir Fabrik wie folgt verwenden:
Wichtige rWertreferenz-Eigenschaften :
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.std::move
Anruf erforderlich ist in:foo&& r = foo(); foo f = std::move(r);
quelle
Named rvalue references are lvalues. Unnamed rvalue references are rvalues.
; Ohne dies zu wissen, habe ich mich bemüht zu verstehen, warum MenschenT &&t; std::move(t);
lange Zeit in Bewegungsabläufen arbeiten und dergleichen.int x; int &&rrx = x;
nicht mehr in GCC kompiliert )typename identity<T>::type& a
gleichbedeutend mitT&
?Es bezeichnet eine Wertreferenz. R-Wert-Referenzen werden nur an temporäre Objekte gebunden, sofern nicht ausdrücklich anders generiert. Sie werden verwendet, um Objekte unter bestimmten Umständen wesentlich effizienter zu gestalten und eine Funktion bereitzustellen, die als perfekte Weiterleitung bezeichnet wird und den Vorlagencode erheblich vereinfacht.
In C ++ 03 können Sie nicht zwischen einer Kopie eines nicht veränderlichen l-Werts und einem r-Wert unterscheiden.
In C ++ 0x ist dies nicht der Fall.
Betrachten Sie die Implementierung hinter diesen Konstruktoren. Im ersten Fall muss die Zeichenfolge eine Kopie ausführen, um die Wertesemantik beizubehalten, was eine neue Heap-Zuordnung beinhaltet. Im zweiten Fall wissen wir jedoch im Voraus, dass das Objekt, das an unseren Konstruktor übergeben wurde, sofort zerstört werden muss und nicht unberührt bleiben muss. In diesem Szenario, das wesentlich effizienter ist, können wir effektiv nur die internen Zeiger austauschen und überhaupt kein Kopieren durchführen. Die Verschiebungssemantik kommt jeder Klasse zugute, die das Kopieren von intern referenzierten Ressourcen teuer oder verboten hat. Betrachten Sie den Fall von
std::unique_ptr
- jetzt, da unsere Klasse zwischen temporären und nicht temporären unterscheiden kann, können wir die Verschiebungssemantik korrekt ausführen, sodass dieunique_ptr
nicht kopiert, sondern verschoben werden kann, was bedeutet, dassstd::unique_ptr
kann legal in Standardcontainern gespeichert, sortiert usw. werden, C ++ 03std::auto_ptr
nicht.Nun betrachten wir die andere Verwendung von rWert-Referenzen - perfekte Weiterleitung. Betrachten Sie die Frage der Bindung eines Verweises an einen Verweis.
Ich kann mich nicht erinnern, was C ++ 03 dazu sagt, aber in C ++ 0x ist der resultierende Typ beim Umgang mit rWert-Referenzen kritisch. Eine r-Wert-Referenz auf einen Typ T, wobei T ein Referenztyp ist, wird zu einer Referenz vom Typ T.
Betrachten Sie die einfachste Vorlagenfunktion min und max. In C ++ 03 müssen Sie alle vier Kombinationen von const und non-const manuell überladen. In C ++ 0x ist es nur eine Überladung. In Kombination mit verschiedenen Vorlagen ermöglicht dies eine perfekte Weiterleitung.
Ich habe den Abzug vom Rückgabetyp weggelassen, weil ich mich nicht erinnern kann, wie es ohne weiteres gemacht wurde, aber diese min kann jede Kombination von l-Werten, r-Werten und konstanten Werten akzeptieren.
quelle
std::forward<A>(aref) < std::forward<B>(bref)
? und ich glaube nicht, dass diese Definition korrekt sein wird, wenn Sie vorwärtsint&
und versuchenfloat&
. Legen Sie besser eine Formularvorlage ab.Der Begriff für die
T&&
Verwendung mit Typabzug (z. B. für die perfekte Weiterleitung) wird umgangssprachlich als Weiterleitungsreferenz bezeichnet . Der Begriff "universelle Referenz" wurde von Scott Meyers in diesem Artikel geprägt , aber später geändert.Das liegt daran, dass es entweder ein r-Wert oder ein l-Wert sein kann.
Beispiele sind:
Weitere Informationen finden Sie in der Antwort für: Syntax für universelle Referenzen
quelle
Eine r-Wert-Referenz ist ein Typ, der sich mit mehreren Ausnahmen ähnlich wie die normale Referenz X & verhält. Das Wichtigste ist, dass lvalues bei der Auflösung von Funktionsüberlastungen alte lvalue-Referenzen bevorzugen, während rvalues die neuen rvalue-Referenzen bevorzugen:
Was ist ein Wert? Alles, was kein Wert ist. Ein l-Wert ist ein Ausdruck, der sich auf einen Speicherort bezieht und es uns ermöglicht, die Adresse dieses Speicherorts über den Operator & zu ermitteln.
Es ist fast einfacher, zuerst zu verstehen, was Werte mit einem Beispiel erreichen:
Der Konstruktor und die Zuweisungsoperatoren wurden mit Versionen überladen, die rWertreferenzen enthalten. Mit R-Wert-Referenzen kann eine Funktion zur Kompilierungszeit (über die Überlastungsauflösung) unter der Bedingung "Wird ich für einen l-Wert oder einen r-Wert aufgerufen?" Verzweigen. Dies ermöglichte es uns, effizientere Konstruktor- und Zuweisungsoperatoren darüber zu erstellen, die Ressourcen verschieben, anstatt sie zu kopieren.
Der Compiler verzweigt automatisch zur Kompilierungszeit (abhängig davon, ob er für einen l-Wert oder einen r-Wert aufgerufen wird) und wählt aus, ob der Verschiebungskonstruktor oder der Verschiebungszuweisungsoperator aufgerufen werden soll.
Zusammenfassend lässt sich sagen, dass rWertreferenzen eine Verschiebungssemantik ermöglichen (und eine perfekte Weiterleitung, die im folgenden Artikellink beschrieben wird).
Ein praktisches, leicht verständliches Beispiel ist die Klassenvorlage std :: unique_ptr . Da ein unique_ptr das ausschließliche Eigentum an seinem zugrunde liegenden Rohzeiger behält, können unique_ptrs nicht kopiert werden. Das würde ihre Invariante des ausschließlichen Eigentums verletzen. Sie haben also keine Kopierkonstruktoren. Aber sie haben Verschiebungskonstruktoren:
static_cast<unique_ptr<int[]>&&>(ptr)
wird normalerweise mit std :: move durchgeführtEin ausgezeichneter Artikel, der all dies und mehr erklärt (z. B. wie rvalues eine perfekte Weiterleitung ermöglichen und was dies bedeutet), mit vielen guten Beispielen, ist Thomas Beckers C ++ Rvalue References Explained . Dieser Beitrag stützte sich stark auf seinen Artikel.
Eine kürzere Einführung ist eine kurze Einführung in Rvalue References von Stroutrup et al. al
quelle
Sample(const Sample& s)
auch den Inhalt kopieren muss? Die gleiche Frage für den 'Kopierzuweisungsoperator'.