Ich habe gerade festgestellt, dass ich die Logik von nicht vollständig verstanden habe std::move()
.
Zuerst habe ich es gegoogelt, aber es scheint nur Dokumente zu geben, wie man es benutzt std::move()
, nicht wie seine Struktur funktioniert.
Ich meine, ich weiß, was die Vorlagenelementfunktion ist, aber wenn ich mir die std::move()
Definition in VS2010 anschaue, ist sie immer noch verwirrend.
Die Definition von std :: move () ist unten aufgeführt.
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}
Was mir zuerst komisch ist, ist der Parameter (_Ty && _Arg), denn wenn ich die Funktion aufrufe, wie Sie unten sehen,
// main()
Object obj1;
Object obj2 = std::move(obj1);
es ist im Grunde gleich
// std::move()
_Ty&& _Arg = Obj1;
Aber wie Sie bereits wissen, können Sie einen LValue nicht direkt mit einer RValue-Referenz verknüpfen, weshalb ich denke, dass dies so sein sollte.
_Ty&& _Arg = (Object&&)obj1;
Dies ist jedoch absurd, da std :: move () für alle Werte funktionieren muss.
Um zu verstehen, wie das funktioniert, sollte ich mir auch diese Strukturen ansehen.
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&>
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&&>
{ // remove rvalue reference
typedef _Ty _Type;
};
Leider ist es immer noch so verwirrend und ich verstehe es nicht.
Ich weiß, dass dies alles auf meinen Mangel an grundlegenden Syntaxkenntnissen in C ++ zurückzuführen ist. Ich würde gerne wissen, wie diese gründlich funktionieren, und alle Dokumente, die ich im Internet erhalten kann, werden mehr als begrüßt. (Wenn Sie dies nur erklären können, wird das auch fantastisch sein).
quelle
move
funktioniert und nicht wie es implementiert wird. Ich finde diese Erklärung wirklich nützlich: pagefault.blog/2018/03/01/… .Antworten:
Wir beginnen mit der Verschiebungsfunktion (die ich ein wenig aufgeräumt habe):
Beginnen wir mit dem einfacheren Teil - das heißt, wenn die Funktion mit rvalue aufgerufen wird:
und unsere
move
Vorlage wird wie folgt instanziiert:Da
remove_reference
konvertiertT&
zuT
oderT&&
zuT
undObject
keine Referenz ist, lautet unsere letzte Funktion:Nun fragen Sie sich vielleicht: Brauchen wir überhaupt die Besetzung? Die Antwort lautet: Ja, das tun wir. Der Grund ist einfach; Die benannte rWertreferenz wird als lWert behandelt (und die implizite Konvertierung von lWert in rWertreferenz ist standardmäßig verboten).
move
Folgendes passiert, wenn wir mit lvalue aufrufen:und entsprechende
move
Instanziierung:Wieder
remove_reference
konvertiertObject&
zuObject
und wir bekommen:Jetzt kommen wir zum kniffligen Teil: Was bedeutet das
Object& &&
überhaupt und wie kann es an den Wert binden?Um eine perfekte Weiterleitung zu ermöglichen, bietet der C ++ 11-Standard spezielle Regeln für das Reduzieren von Referenzen:
Wie Sie sehen können, bedeutet unter diesen Regeln
Object& &&
tatsächlichObject&
, dass es sich um eine einfache L-Wert-Referenz handelt, die das Binden von L-Werten ermöglicht.Die letzte Funktion ist also:
Das ist nicht anders als bei der vorherigen Instanziierung mit rvalue - beide werfen ihr Argument auf die rvalue-Referenz und geben es dann zurück. Der Unterschied besteht darin, dass die erste Instanziierung nur mit r-Werten verwendet werden kann, während die zweite mit l-Werten arbeitet.
Um zu erklären, warum wir
remove_reference
etwas mehr brauchen , versuchen wir diese Funktionund instanziiere es mit lvalue.
Wenn Sie die oben erwähnten Regeln zum
move
Reduzieren von Referenzen anwenden, können Sie sehen, dass wir eine Funktion erhalten, die unbrauchbar ist (um es einfach auszudrücken, Sie rufen sie mit lvalue auf, Sie erhalten lvalue zurück). Wenn überhaupt, ist diese Funktion die Identitätsfunktion.quelle
T
als zu bewertenObject&
, wusste ich nicht, dass dies wirklich getan wird. Ich hätte erwartetT
, dies auchObject
in diesem Fall zu bewerten , da ich dachte, dies sei der Grund für die Einführung von Wrapper-Referenzen undstd::ref
oder nicht.template <typename T> void f(T arg)
(was im Wikipedia-Artikel steht) undtemplate <typename T> void f(T& arg)
. Der erste wird in Wert aufgelöst (und wenn Sie eine Referenz übergeben möchten, müssen Sie ihn einschließenstd::ref
), während der zweite immer in Referenz aufgelöst wird. Leider sind die Regeln für die Ableitung von Vorlagenargumenten ziemlich komplex, so dass ich keine genauenT&&
Gründe dafür liefern kann, warum sie sich ändernObject& &&
(aber es passiert tatsächlich).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
nur l-Werte in r-Werte umwandeln willst, dannT&
wäre das in Ordnung. Dieser Trick wird hauptsächlich ausstd::move
Gründen der Flexibilität ausgeführt: Sie können alles aufrufen (einschließlich rWerte) und rWert zurückerhalten._Ty ist in dieser Situation ein Vorlagenparameter
_Ty ist der Typ "Object &"
Aus diesem Grund ist die Referenz "Entfernen" erforderlich.
Es wäre eher so
Wenn wir die Referenz nicht entfernen würden, wäre es so, wie wir es tun würden
ObjectRef && reduziert sich jedoch auf Object &, das wir nicht an obj2 binden konnten.
Der Grund dafür ist die Unterstützung einer perfekten Weiterleitung. Siehe dieses Papier .
quelle
_Remove_reference_
notwendig ist. Wenn SieObject&
beispielsweise ein typedef haben und einen Verweis darauf nehmen, erhalten Sie immer nochObject&
. Warum funktioniert das nicht mit &&? Darauf gibt es eine Antwort, die mit perfekter Weiterleitung zu tun hat.