Diese Frage bezieht sich auf std::move(T && t); Es gibt auch std::move(InputIt first, InputIt last, OutputIt d_first)einen Algorithmus, auf den sich ein Algorithmus bezieht std::copy. Ich weise darauf hin, damit andere nicht so verwirrt sind wie ich, als ich zum ersten Mal mit std::movedrei Argumenten konfrontiert wurde . en.cppreference.com/w/cpp/algorithm/move
In C ++ 11 können Objekte zusätzlich zu Kopierkonstruktoren Verschiebungskonstruktoren haben.
(Zusätzlich zu den Kopierzuweisungsoperatoren gibt es Verschiebungszuweisungsoperatoren.)
Der Verschiebungskonstruktor wird anstelle des Kopierkonstruktors verwendet, wenn das Objekt den Typ "rvalue-reference" ( Type &&) hat.
std::move() ist eine Besetzung, die eine r-Wert-Referenz auf ein Objekt erzeugt, um das Bewegen von diesem zu ermöglichen.
Es ist eine neue C ++ - Methode, um Kopien zu vermeiden. Wenn Sie beispielsweise einen Verschiebungskonstruktor verwenden, kann a std::vectoreinfach seinen internen Zeiger auf Daten in das neue Objekt kopieren und das verschobene Objekt in einem verschobenen Zustand belassen, sodass nicht alle Daten kopiert werden. Dies wäre C ++ - gültig.
Versuchen Sie, nach Bewegungssemantik, Wert und perfekter Weiterleitung zu googeln.
Für die Verschiebungssemantik muss das verschobene Objekt gültig bleiben , was kein falscher Zustand ist. (Begründung: Es muss noch zerstören, damit es funktioniert.)
GManNickG
13
@GMan: Nun, es muss sich in einem Zustand befinden, der sicher zu zerstören ist, aber AFAIK, es muss für nichts anderes verwendbar sein.
Zan Lynx
8
@ ZanLynx: Richtig. Beachten Sie, dass für die Standardbibliothek zusätzlich verschobene Objekte zuweisbar sein müssen. Dies gilt jedoch nur für Objekte, die in der stdlib verwendet werden. Dies ist keine allgemeine Anforderung.
GManNickG
24
-1 "std :: move () ist die C ++ 11-Methode zur Verwendung der Verschiebungssemantik" Bitte beheben Sie das. std::move()ist nicht die Möglichkeit, die Verschiebungssemantik zu verwenden. Die Verschiebungssemantik wird für den Programmierer transparent ausgeführt. moveEs ist nur eine Umwandlung, um einen Wert von einem Punkt zum anderen zu übergeben, wo der ursprüngliche Wert nicht mehr verwendet wird.
Manu343726
15
Ich würde weiter gehen. std::moveselbst macht "nichts" - es hat keine Nebenwirkungen. Es signalisiert dem Compiler lediglich, dass es dem Programmierer egal ist, was mit diesem Objekt passiert. Das heißt, es gibt anderen Teilen der Software die Erlaubnis , sich vom Objekt zu entfernen, es muss jedoch nicht verschoben werden. Tatsächlich muss der Empfänger einer rWert-Referenz keine Zusagen machen, was er mit den Daten tun wird oder nicht.
Aaron McDaid
240
1. "Was ist das?"
Während std::move() technisch eine Funktion ist - ich würde sagen, es ist nicht wirklich eine Funktion . Es ist eine Art Konverter zwischen den Methoden, mit denen der Compiler den Wert eines Ausdrucks berücksichtigt.
2. "Was macht es?"
Das erste, was zu beachten ist, ist, dass std::move()sich nichts bewegt . Es konvertiert einen Ausdruck von einem l-Wert (z. B. einer benannten Variablen) in einen x-Wert . Ein xvalue teilt dem Compiler mit:
Du kannst mich plündern, alles , was ich halte , bewegen und woanders verwenden (da ich sowieso bald zerstört werde) ".
Mit anderen Worten, wenn Sie verwenden std::move(x), lassen Sie den Compiler ausschlachten x. Wenn xalso beispielsweise ein eigener Puffer im Speicher vorhanden ist, std::move()kann der Compiler nach dem Erstellen stattdessen ein anderes Objekt besitzen.
Sie können auch von einem Wert wechseln (z. B. von einem temporären Wert, den Sie herumgeben ), dies ist jedoch selten nützlich.
3. "Wann sollte es verwendet werden?"
Eine andere Möglichkeit, diese Frage zu stellen, lautet: "Wofür kann ich die Ressourcen eines vorhandenen Objekts ausschlachten?" Wenn Sie Anwendungscode schreiben, werden Sie wahrscheinlich nicht viel mit temporären Objekten herumspielen, die vom Compiler erstellt wurden. Sie würden dies also hauptsächlich an Orten wie Konstruktoren, Operatormethoden, Standardbibliotheksalgorithmus-ähnlichen Funktionen usw. tun, an denen Objekte häufig automatisch erstellt und zerstört werden. Das ist natürlich nur eine Faustregel.
Eine typische Verwendung besteht darin, Ressourcen von einem Objekt zu einem anderen zu verschieben, anstatt sie zu kopieren. @ Guillaume verlinkt auf diese Seite, die ein einfaches kurzes Beispiel enthält: Vertauschen von zwei Objekten mit weniger Kopieraufwand.
template<class T>
swap(T& a, T& b){
T tmp(a);// we now have two copies of a
a = b;// we now have two copies of b (+ discarded a copy of a)
b = tmp;// we now have two copies of tmp (+ discarded a copy of b)}
Mit move können Sie die Ressourcen austauschen, anstatt sie zu kopieren:
template<class T>
swap(T& a, T& b){
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);}
Denken Sie daran, was passiert, wenn Tes beispielsweise die vector<int>Größe n hat. In der ersten Version lesen und schreiben Sie 3 * n Elemente, in der zweiten Version lesen und schreiben Sie im Grunde nur die 3 Zeiger auf die Vektorpuffer plus die 3 Puffergrößen. Natürlich muss die Klasse Twissen, wie man sich bewegt; Ihre Klasse sollte einen Verschiebungszuweisungsoperator und einen Verschiebungskonstruktor für die Klasse haben, Tdamit dies funktioniert.
Ich habe lange von dieser Bewegungssemantik gehört und sie nie untersucht. Aus dieser Beschreibung geht hervor, dass es sich nur um eine flache Kopie statt um eine tiefe Kopie handelt.
Zebrafisch
7
@TitoneMaurice: Nur dass es sich nicht um eine Kopie handelt, da der ursprüngliche Wert nicht mehr verwendet werden kann.
Einpoklum
3
@Zebrafish du könntest nicht falscher sein. Eine flache Kopie belässt das Original im exakt gleichen Zustand. Eine Verschiebung führt normalerweise dazu, dass das Original leer oder in einem anderweitig gültigen Zustand ist.
Rubenvb
16
@rubenvb Zebra ist nicht ganz falsch. Zwar wird das ursprüngliche cannabilisierte Objekt normalerweise absichtlich sabotiert, um verwirrende Fehler zu vermeiden (z. B. setzen Sie seine Zeiger auf nullptr, um zu signalisieren, dass es die Zeiger nicht mehr besitzt), aber die Tatsache, dass die gesamte Bewegung durch einfaches Kopieren eines Zeigers aus der Quelle implementiert wird zum Ziel zu gelangen (und absichtlich zu vermeiden, etwas mit dem Pointee zu tun), erinnert in der Tat an eine flache Kopie. In der Tat würde ich gehen sogar so weit zu sagen , dass ein Umzug ist eine flache Kopie, gegebenenfalls gefolgt von einer teilweisen Selbstzerstörung der Quelle. (Forts.)
Leichtigkeitsrennen im Orbit
3
(Forts.) Wenn wir diese Definition zulassen (und ich mag sie eher), dann ist die Beobachtung von @ Zebrafish nicht falsch, nur etwas unvollständig.
Leichtigkeitsrennen im Orbit
144
Sie können move verwenden, wenn Sie den Inhalt eines Objekts an eine andere Stelle "übertragen" müssen, ohne eine Kopie zu erstellen (dh der Inhalt wird nicht dupliziert, daher kann er für einige nicht kopierbare Objekte wie unique_ptr verwendet werden). Mit std :: move kann ein Objekt auch den Inhalt eines temporären Objekts übernehmen, ohne eine Kopie zu erstellen (und viel Zeit zu sparen).
Es tut mir leid, wenn meine Antwort zu spät kommt, aber ich habe auch nach einem guten Link für den std :: move gesucht und die obigen Links etwas "streng" gefunden.
Dies legt den Schwerpunkt auf die R-Wert-Referenz, in welchem Kontext Sie sie verwenden sollten, und ich denke, es ist detaillierter, deshalb wollte ich diesen Link hier teilen.
Netter Link. Ich fand den Wikipedia-Artikel und andere Links, über die ich gestolpert bin, immer ziemlich verwirrend, da sie nur Fakten auf Sie werfen und es Ihnen überlassen, herauszufinden, was die tatsächliche Bedeutung / Begründung ist. Während "Verschiebungssemantik" in einem Konstruktor ziemlich offensichtlich ist, sind all diese Details zum Übergeben von && - Werten nicht ... daher war die Beschreibung im Tutorial-Stil sehr schön.
Christian Stieber
66
F: Was ist std::move?
A: std::move()ist eine Funktion aus der C ++ - Standardbibliothek zum Umwandeln in eine rvalue-Referenz.
Ein r-Wert ist ein temporärer Wert, der nicht über den Ausdruck hinaus bestehen bleibt, der ihn definiert, z. B. ein Ergebnis einer Zwischenfunktion, das niemals in einer Variablen gespeichert wird.
int a =3;// 3 is a rvalue, does not exist after expression is evaluatedint b = a;// a is a lvalue, keeps existing after expression is evaluated
Wie Sie sehen können, std::movekehrt T&&unabhängig davon , ob mit einem Wert (genannt T), Referenz - Typ ( T&) oder R - Wert - Referenz ( T&&).
F: Was macht es?
A: Als Cast macht es zur Laufzeit nichts. Es ist nur zur Kompilierungszeit relevant, dem Compiler mitzuteilen, dass Sie die Referenz weiterhin als Wert betrachten möchten.
foo(3*5);// obviously, you are calling foo with a temporary (rvalue)int a =3*5;
foo(a);// how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a));// will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`
Was es nicht macht:
Machen Sie eine Kopie des Arguments
Rufen Sie den Kopierkonstruktor auf
Ändern Sie das Argumentobjekt
F: Wann sollte es verwendet werden?
A: Sie sollten verwenden, std::movewenn Sie Funktionen aufrufen möchten, die die Verschiebungssemantik mit einem Argument unterstützen, das kein r-Wert ist (temporärer Ausdruck).
Dies wirft für mich folgende Anschlussfragen auf:
Was ist Bewegungssemantik? Die Verschiebungssemantik im Gegensatz zur Kopiersemantik ist eine Programmiertechnik, bei der die Elemente eines Objekts durch "Übernehmen" initialisiert werden, anstatt die Elemente eines anderen Objekts zu kopieren. Eine solche Übernahme ist nur bei Zeigern und Ressourcenhandles sinnvoll, die kostengünstig übertragen werden können, indem der Zeiger oder das Integer-Handle anstelle der zugrunde liegenden Daten kopiert werden.
Welche Klassen und Objekte unterstützen die Bewegungssemantik? Es liegt an Ihnen als Entwickler, die Verschiebungssemantik in Ihren eigenen Klassen zu implementieren, wenn diese von der Übertragung ihrer Mitglieder profitieren würden, anstatt sie zu kopieren. Sobald Sie die Verschiebungssemantik implementiert haben, profitieren Sie direkt von der Arbeit vieler Bibliotheksprogrammierer, die Unterstützung für die effiziente Behandlung von Klassen mit Verschiebungssemantik hinzugefügt haben.
Warum kann der Compiler es nicht selbst herausfinden? Der Compiler kann nicht einfach eine weitere Überladung einer Funktion aufrufen, es sei denn, Sie sagen dies. Sie müssen dem Compiler bei der Auswahl helfen, ob die reguläre oder die Verschiebungsversion der Funktion aufgerufen werden soll.
In welchen Situationen möchte ich dem Compiler mitteilen, dass er eine Variable als rWert behandeln soll? Dies geschieht höchstwahrscheinlich in Vorlagen- oder Bibliotheksfunktionen, bei denen Sie wissen, dass ein Zwischenergebnis gerettet werden kann.
Big +1 für Codebeispiele mit Semantik in Kommentaren. Die anderen Top-Antworten definieren std :: move mit "move" selbst - klärt nichts wirklich! --- Ich glaube, es ist erwähnenswert, dass das Fehlen einer Kopie des Arguments bedeutet, dass der ursprüngliche Wert nicht zuverlässig verwendet werden kann.
ty
34
std :: move selbst macht nicht wirklich viel. Ich dachte, dass es den verschobenen Konstruktor für ein Objekt aufruft, aber es führt wirklich nur eine Typumwandlung durch (Umwandlung einer lvalue-Variablen in einen rvalue, damit diese Variable als Argument an einen Verschiebungskonstruktor oder einen Zuweisungsoperator übergeben werden kann).
Daher wird std :: move nur als Vorläufer für die Verwendung der Verschiebungssemantik verwendet. Die Verschiebungssemantik ist im Wesentlichen eine effiziente Methode für den Umgang mit temporären Objekten.
Betrachten Sie das Objekt A = B + C + D + E + F;
Das ist gut aussehender Code, aber E + F erzeugt ein temporäres Objekt. Dann erzeugt D + temp ein anderes temporäres Objekt und so weiter. In jedem normalen "+" - Operator einer Klasse treten tiefe Kopien auf.
Zum Beispiel
ObjectObject::operator+(constObject& rhs){Object temp (*this);// logic for addingreturn temp;}
Die Erstellung des temporären Objekts in dieser Funktion ist nutzlos - diese temporären Objekte werden ohnehin am Ende der Zeile gelöscht, wenn sie den Gültigkeitsbereich verlassen.
Wir können lieber die Bewegungssemantik verwenden, um die temporären Objekte zu "plündern" und so etwas zu tun
Object&Object::operator+(Object&& rhs){// logic to modify rhs directlyreturn rhs;}
Dadurch wird vermieden, dass unnötig tiefe Kopien erstellt werden. In Bezug auf das Beispiel ist der einzige Teil, in dem tiefes Kopieren stattfindet, jetzt E + F. Der Rest verwendet die Bewegungssemantik. Der Verschiebungskonstruktor oder Zuweisungsoperator muss ebenfalls implementiert werden, um das Ergebnis A zuzuweisen.
Sie haben über Bewegungssemantik gesprochen. Sie sollten Ihrer Antwort hinzufügen, wie std :: move verwendet werden kann, da die Frage danach fragt.
Koushik Shetty
2
@Koushik std :: move macht nicht viel - wird aber verwendet, um die Bewegungssemantik zu implementieren. Wenn Sie nicht über std :: move Bescheid wissen, kennen Sie wahrscheinlich auch die Verschiebungssemantik nicht
user929404
1
"macht nicht viel" (ja nur ein static_cast zu einer rvalue referenz). Was es tatsächlich tut und was es tut, ist das, was das OP gefragt hat. Sie müssen nicht wissen, wie std :: move funktioniert, aber Sie müssen wissen, was die Bewegungssemantik bewirkt. Darüber hinaus ist "aber wird verwendet, um die Bewegungssemantik zu implementieren" umgekehrt. Kennen Sie die Bewegungssemantik und Sie werden std :: move verstehen, sonst nein. move hilft nur bei der Bewegung und verwendet selbst die Bewegungssemantik. std :: move konvertiert sein Argument nur in eine r-Wert-Referenz, was für die Bewegungssemantik erforderlich ist.
Koushik Shetty
10
"aber E + F erzeugt ein temporäres Objekt" - Der Operator +geht von links nach rechts, nicht von rechts nach links. Daher B+Cwäre zuerst!
Ajay
8
"Was ist es?" und "Was macht es?" wurde oben erklärt.
Ich werde ein Beispiel geben, "wann es verwendet werden sollte".
Zum Beispiel haben wir eine Klasse mit vielen Ressourcen wie einem großen Array.
classResHeavy{// ResHeavy means heavy resourcepublic:ResHeavy(int len=10):_upInt(newint[len]),_len(len){
cout<<"default ctor"<<endl;}ResHeavy(constResHeavy& rhs):_upInt(newint[rhs._len]),_len(rhs._len){
cout<<"copy ctor"<<endl;}ResHeavy&operator=(constResHeavy& rhs){
_upInt.reset(newint[rhs._len]);
_len = rhs._len;
cout<<"operator= ctor"<<endl;}ResHeavy(ResHeavy&& rhs){
_upInt = std::move(rhs._upInt);
_len = rhs._len;
rhs._len =0;
cout<<"move ctor"<<endl;}// check array validbool is_up_valid(){return _upInt !=nullptr;}private:
std::unique_ptr<int[]> _upInt;// heavy array resourceint _len;// length of int array};
Testcode:
void test_std_move2(){ResHeavy rh;// only one int[]// operator rh// after some operator of rh, it becomes no-use// transform it to other objectResHeavy rh2 = std::move(rh);// rh becomes invalid// show rh, rh2 it validif(rh.is_up_valid())
cout<<"rh valid"<<endl;else
cout<<"rh invalid"<<endl;if(rh2.is_up_valid())
cout<<"rh2 valid"<<endl;else
cout<<"rh2 invalid"<<endl;// new ResHeavy object, created by copy ctorResHeavy rh3(rh2);// two copy of int[]if(rh3.is_up_valid())
cout<<"rh3 valid"<<endl;else
cout<<"rh3 invalid"<<endl;}
Wir können sehen, dass sich std::movemit move constructormake die Ressource leicht transformieren lässt.
Wo sonst ist std::movenützlich?
std::movekann auch beim Sortieren eines Arrays von Elementen hilfreich sein. Viele Sortieralgorithmen (wie Auswahlsortierung und Blasensortierung) tauschen Elementpaare aus. Bisher mussten wir auf die Kopiersemantik zurückgreifen, um den Austausch durchzuführen. Jetzt können wir die Verschiebungssemantik verwenden, die effizienter ist.
Dies kann auch nützlich sein, wenn Sie den von einem intelligenten Zeiger verwalteten Inhalt auf einen anderen verschieben möchten.
std::move(T && t)
; Es gibt auchstd::move(InputIt first, InputIt last, OutputIt d_first)
einen Algorithmus, auf den sich ein Algorithmus beziehtstd::copy
. Ich weise darauf hin, damit andere nicht so verwirrt sind wie ich, als ich zum ersten Mal mitstd::move
drei Argumenten konfrontiert wurde . en.cppreference.com/w/cpp/algorithm/moveAntworten:
Wikipedia-Seite zu C ++ 11 R-Wert-Referenzen und Verschiebungskonstruktoren
(Zusätzlich zu den Kopierzuweisungsoperatoren gibt es Verschiebungszuweisungsoperatoren.)
Type &&
) hat.std::move()
ist eine Besetzung, die eine r-Wert-Referenz auf ein Objekt erzeugt, um das Bewegen von diesem zu ermöglichen.Es ist eine neue C ++ - Methode, um Kopien zu vermeiden. Wenn Sie beispielsweise einen Verschiebungskonstruktor verwenden, kann a
std::vector
einfach seinen internen Zeiger auf Daten in das neue Objekt kopieren und das verschobene Objekt in einem verschobenen Zustand belassen, sodass nicht alle Daten kopiert werden. Dies wäre C ++ - gültig.Versuchen Sie, nach Bewegungssemantik, Wert und perfekter Weiterleitung zu googeln.
quelle
std::move()
ist nicht die Möglichkeit, die Verschiebungssemantik zu verwenden. Die Verschiebungssemantik wird für den Programmierer transparent ausgeführt.move
Es ist nur eine Umwandlung, um einen Wert von einem Punkt zum anderen zu übergeben, wo der ursprüngliche Wert nicht mehr verwendet wird.std::move
selbst macht "nichts" - es hat keine Nebenwirkungen. Es signalisiert dem Compiler lediglich, dass es dem Programmierer egal ist, was mit diesem Objekt passiert. Das heißt, es gibt anderen Teilen der Software die Erlaubnis , sich vom Objekt zu entfernen, es muss jedoch nicht verschoben werden. Tatsächlich muss der Empfänger einer rWert-Referenz keine Zusagen machen, was er mit den Daten tun wird oder nicht.1. "Was ist das?"
Während
std::move()
technisch eine Funktion ist - ich würde sagen, es ist nicht wirklich eine Funktion . Es ist eine Art Konverter zwischen den Methoden, mit denen der Compiler den Wert eines Ausdrucks berücksichtigt.2. "Was macht es?"
Das erste, was zu beachten ist, ist, dass
std::move()
sich nichts bewegt . Es konvertiert einen Ausdruck von einem l-Wert (z. B. einer benannten Variablen) in einen x-Wert . Ein xvalue teilt dem Compiler mit:Mit anderen Worten, wenn Sie verwenden
std::move(x)
, lassen Sie den Compiler ausschlachtenx
. Wennx
also beispielsweise ein eigener Puffer im Speicher vorhanden ist,std::move()
kann der Compiler nach dem Erstellen stattdessen ein anderes Objekt besitzen.Sie können auch von einem Wert wechseln (z. B. von einem temporären Wert, den Sie herumgeben ), dies ist jedoch selten nützlich.
3. "Wann sollte es verwendet werden?"
Eine andere Möglichkeit, diese Frage zu stellen, lautet: "Wofür kann ich die Ressourcen eines vorhandenen Objekts ausschlachten?" Wenn Sie Anwendungscode schreiben, werden Sie wahrscheinlich nicht viel mit temporären Objekten herumspielen, die vom Compiler erstellt wurden. Sie würden dies also hauptsächlich an Orten wie Konstruktoren, Operatormethoden, Standardbibliotheksalgorithmus-ähnlichen Funktionen usw. tun, an denen Objekte häufig automatisch erstellt und zerstört werden. Das ist natürlich nur eine Faustregel.
Eine typische Verwendung besteht darin, Ressourcen von einem Objekt zu einem anderen zu verschieben, anstatt sie zu kopieren. @ Guillaume verlinkt auf diese Seite, die ein einfaches kurzes Beispiel enthält: Vertauschen von zwei Objekten mit weniger Kopieraufwand.
Mit move können Sie die Ressourcen austauschen, anstatt sie zu kopieren:
Denken Sie daran, was passiert, wenn
T
es beispielsweise dievector<int>
Größe n hat. In der ersten Version lesen und schreiben Sie 3 * n Elemente, in der zweiten Version lesen und schreiben Sie im Grunde nur die 3 Zeiger auf die Vektorpuffer plus die 3 Puffergrößen. Natürlich muss die KlasseT
wissen, wie man sich bewegt; Ihre Klasse sollte einen Verschiebungszuweisungsoperator und einen Verschiebungskonstruktor für die Klasse haben,T
damit dies funktioniert.quelle
Sie können move verwenden, wenn Sie den Inhalt eines Objekts an eine andere Stelle "übertragen" müssen, ohne eine Kopie zu erstellen (dh der Inhalt wird nicht dupliziert, daher kann er für einige nicht kopierbare Objekte wie unique_ptr verwendet werden). Mit std :: move kann ein Objekt auch den Inhalt eines temporären Objekts übernehmen, ohne eine Kopie zu erstellen (und viel Zeit zu sparen).
Dieser Link hat mir wirklich geholfen:
http://thbecker.net/articles/rvalue_references/section_01.html
Es tut mir leid, wenn meine Antwort zu spät kommt, aber ich habe auch nach einem guten Link für den std :: move gesucht und die obigen Links etwas "streng" gefunden.
Dies legt den Schwerpunkt auf die R-Wert-Referenz, in welchem Kontext Sie sie verwenden sollten, und ich denke, es ist detaillierter, deshalb wollte ich diesen Link hier teilen.
quelle
F: Was ist
std::move
?A:
std::move()
ist eine Funktion aus der C ++ - Standardbibliothek zum Umwandeln in eine rvalue-Referenz.Einfach ausgedrückt
std::move(t)
ist gleichbedeutend mit:Ein r-Wert ist ein temporärer Wert, der nicht über den Ausdruck hinaus bestehen bleibt, der ihn definiert, z. B. ein Ergebnis einer Zwischenfunktion, das niemals in einer Variablen gespeichert wird.
Eine Implementierung für std :: move () finden Sie in N2027: "Eine kurze Einführung in Rvalue-Referenzen" wie folgt:
Wie Sie sehen können,
std::move
kehrtT&&
unabhängig davon , ob mit einem Wert (genanntT
), Referenz - Typ (T&
) oder R - Wert - Referenz (T&&
).F: Was macht es?
A: Als Cast macht es zur Laufzeit nichts. Es ist nur zur Kompilierungszeit relevant, dem Compiler mitzuteilen, dass Sie die Referenz weiterhin als Wert betrachten möchten.
Was es nicht macht:
F: Wann sollte es verwendet werden?
A: Sie sollten verwenden,
std::move
wenn Sie Funktionen aufrufen möchten, die die Verschiebungssemantik mit einem Argument unterstützen, das kein r-Wert ist (temporärer Ausdruck).Dies wirft für mich folgende Anschlussfragen auf:
Was ist Bewegungssemantik? Die Verschiebungssemantik im Gegensatz zur Kopiersemantik ist eine Programmiertechnik, bei der die Elemente eines Objekts durch "Übernehmen" initialisiert werden, anstatt die Elemente eines anderen Objekts zu kopieren. Eine solche Übernahme ist nur bei Zeigern und Ressourcenhandles sinnvoll, die kostengünstig übertragen werden können, indem der Zeiger oder das Integer-Handle anstelle der zugrunde liegenden Daten kopiert werden.
Welche Klassen und Objekte unterstützen die Bewegungssemantik? Es liegt an Ihnen als Entwickler, die Verschiebungssemantik in Ihren eigenen Klassen zu implementieren, wenn diese von der Übertragung ihrer Mitglieder profitieren würden, anstatt sie zu kopieren. Sobald Sie die Verschiebungssemantik implementiert haben, profitieren Sie direkt von der Arbeit vieler Bibliotheksprogrammierer, die Unterstützung für die effiziente Behandlung von Klassen mit Verschiebungssemantik hinzugefügt haben.
Warum kann der Compiler es nicht selbst herausfinden? Der Compiler kann nicht einfach eine weitere Überladung einer Funktion aufrufen, es sei denn, Sie sagen dies. Sie müssen dem Compiler bei der Auswahl helfen, ob die reguläre oder die Verschiebungsversion der Funktion aufgerufen werden soll.
In welchen Situationen möchte ich dem Compiler mitteilen, dass er eine Variable als rWert behandeln soll? Dies geschieht höchstwahrscheinlich in Vorlagen- oder Bibliotheksfunktionen, bei denen Sie wissen, dass ein Zwischenergebnis gerettet werden kann.
quelle
std :: move selbst macht nicht wirklich viel. Ich dachte, dass es den verschobenen Konstruktor für ein Objekt aufruft, aber es führt wirklich nur eine Typumwandlung durch (Umwandlung einer lvalue-Variablen in einen rvalue, damit diese Variable als Argument an einen Verschiebungskonstruktor oder einen Zuweisungsoperator übergeben werden kann).
Daher wird std :: move nur als Vorläufer für die Verwendung der Verschiebungssemantik verwendet. Die Verschiebungssemantik ist im Wesentlichen eine effiziente Methode für den Umgang mit temporären Objekten.
Betrachten Sie das Objekt
A = B + C + D + E + F;
Das ist gut aussehender Code, aber E + F erzeugt ein temporäres Objekt. Dann erzeugt D + temp ein anderes temporäres Objekt und so weiter. In jedem normalen "+" - Operator einer Klasse treten tiefe Kopien auf.
Zum Beispiel
Die Erstellung des temporären Objekts in dieser Funktion ist nutzlos - diese temporären Objekte werden ohnehin am Ende der Zeile gelöscht, wenn sie den Gültigkeitsbereich verlassen.
Wir können lieber die Bewegungssemantik verwenden, um die temporären Objekte zu "plündern" und so etwas zu tun
Dadurch wird vermieden, dass unnötig tiefe Kopien erstellt werden. In Bezug auf das Beispiel ist der einzige Teil, in dem tiefes Kopieren stattfindet, jetzt E + F. Der Rest verwendet die Bewegungssemantik. Der Verschiebungskonstruktor oder Zuweisungsoperator muss ebenfalls implementiert werden, um das Ergebnis A zuzuweisen.
quelle
+
geht von links nach rechts, nicht von rechts nach links. DaherB+C
wäre zuerst!"Was ist es?" und "Was macht es?" wurde oben erklärt.
Ich werde ein Beispiel geben, "wann es verwendet werden sollte".
Zum Beispiel haben wir eine Klasse mit vielen Ressourcen wie einem großen Array.
Testcode:
Ausgabe wie folgt:
Wir können sehen, dass sich
std::move
mitmove constructor
make die Ressource leicht transformieren lässt.Wo sonst ist
std::move
nützlich?std::move
kann auch beim Sortieren eines Arrays von Elementen hilfreich sein. Viele Sortieralgorithmen (wie Auswahlsortierung und Blasensortierung) tauschen Elementpaare aus. Bisher mussten wir auf die Kopiersemantik zurückgreifen, um den Austausch durchzuführen. Jetzt können wir die Verschiebungssemantik verwenden, die effizienter ist.Dies kann auch nützlich sein, wenn Sie den von einem intelligenten Zeiger verwalteten Inhalt auf einen anderen verschieben möchten.
Zitiert:
quelle
Hier ist ein vollständiges Beispiel mit std :: move für einen (einfachen) benutzerdefinierten Vektor
Erwartete Ausgabe:
Kompilieren als:
Code:
quelle