2016-09-07 3 views
11

Gibt es einen Vorteil, eine r-Wert-Referenz zu verwenden, wenn Sie ein Objekt erstellen, das andernfalls in einer normalen lokalen Variablen wäre?Erstellen eines Objekts, lokale Variable vs rvalue Referenz

Foo&& cn = Foo(); 
cn.m_flag = 1; 
bar.m_foo = std::move(cn); 
//cn is not used again 

Foo cn; 
cn.m_flag = 1; 
bar.m_foo = std::move(cn); //Is it ok to move a non rvalue reference? 
//cn is not used again 

Im ersten Code-Schnipsel, so scheint es klar, dass es keine Kopien sein, aber ich würde vermuten, dass in den zweiten die Kompilierung Kopien optimieren würde aus?

Auch im ersten Snippet, wo ist das Objekt tatsächlich im Speicher gespeichert (in der zweiten ist es im Stapelrahmen der umschließenden Funktion gespeichert)?

+0

Ihr erster Teil sollte nicht einmal kompilieren. Der zweite Teil macht genau das, was Sie wollen. std :: move macht cn zu einem rvalue und verschiebt es dann zu m_foo (vorausgesetzt, die move-Operatoren werden nicht gelöscht usw.). – Hayt

+0

@Hayt, warum sollte es nicht kompilieren? Wie ich es verstehe, verlängert das Zuweisen eines temporären zu einem r Wert ref die Lebensdauer des temporären (wenn es eine normale Referenz wäre, dann würde es ja nicht kompilieren) –

+0

Ah ok. habe nie jemanden gesehen, der so codiert hat (es ist nicht wirklich notwendig).Die Bewegung wird jedoch meistens dazu verwendet, ein Nicht-Rvalue-Objekt zu einem R-Wert zu machen. – Hayt

Antwort

8

Diese Codefragmente sind meistens gleichwertig. Dies ist:

bindet eine temporäre an eine Referenz, die die Lebensdauer des temporären auf die der Referenz verlängert Die Foo wird nur zerstört, wenn rf den Gültigkeitsbereich verlässt. Welches ist das gleiche Verhalten, das Sie mit bekommen:

Foo f; 

mit der Ausnahme, dass im letzteren Beispiel f ist default-initialisiert, aber in dem früheren Beispiel rf ist Wert initialisiert. Bei einigen Typen sind die beiden gleichwertig. Für andere sind sie nicht. Wenn Sie stattdessen Foo f{} geschrieben hätten, dann würde dieser Unterschied verschwinden.

Eine verbleibende Differenz betrifft elision kopieren:

Foo give_a_foo_rv() { 
    Foo&& rf = Foo(); 
    return rf; 
} 

Foo give_a_foo() { 
    Foo f{}; 
    return f; 
} 

RVO nicht im ersten Beispiel durchgeführt werden darf, weil rf nicht den gleichen Typ wie der Rückgabetyp give_a_foo_rv() haben. Darüber hinaus wird rf nicht einmal automatisch in den Rückgabetyp verschoben werden, da es kein Objekt ist, so dass es keine automatische Speicherdauer hat, so ist das eine zusätzliche Kopie:

Foo f = give_a_foo_rv(); // a copy happens here! 
Foo g = give_a_foo(); // no move or copy 

es scheint klar, dass es keine Kopien geben wird

Das hängt ganz davon ab, was ein Foo tatsächlich bewegt. Wenn Foo wie folgt aussieht:

struct Foo { 
    Foo() = default; 
    Foo(Foo const&) = default; 
    Foo& operator=(Foo const&) = default; 

    // some members 
}; 

dann eine Foo noch in Bewegung tut Kopieren.


Und ja, es ist vollkommen in Ordnung zu std::move(f) im zweiten Beispiel. Sie brauchen kein Objekt vom Typ rvalue reference zu T zu move von ihm. Dies würde die Nützlichkeit des Bewegens stark einschränken.

Verwandte Themen