2014-01-23 12 views
14

Nehmen wir an, ich habe eine Foo Klasse mit einem std::vector aus std::unique_ptr Objekte einer anderen Klasse, Bar.Warum benötigt dies eine explizite std :: move?

typedef std::unique_ptr<Bar> UniqueBar; 

class Foo { 
    std::vector<UniqueBar> bars; 
public: 
    void AddBar(UniqueBar&& bar); 
}; 

void Foo::AddBar(UniqueBar&& bar) { 
    bars.push_back(bar); 
} 

Dieses eine führt zu einem Übersetzungsfehler (in g ++ 4.8.1) sagen, dass das der Kopierkonstruktor von std::unique_ptr gelöscht wird, was sinnvoll ist. Die Frage hier ist, da das bar-Argument bereits eine rvalue-Referenz ist, warum wird der Kopierkonstruktor von std::unique_ptr anstelle von seinem Move-Konstruktor aufgerufen?

Wenn ich explizit std::move in Foo::AddBar aufrufen, dann geht das Kompilierungsproblem weg, aber ich verstehe nicht, warum dies benötigt wird. Ich denke, es ist ziemlich überflüssig.

Also, was fehlt mir?

Antwort

16

Grundsätzlich ist jedes Objekt, das einen Namen hat, ein Lvalue. Wenn Sie ein Objekt mithilfe eines rvalue-Verweises an eine Funktion übergeben, sieht die Funktion tatsächlich einen Lvalue: Sie wird benannt. Die Rvalue-Referenz gibt jedoch an, dass sie von einem Objekt stammt, das bereit ist, übertragen zu werden.

anders ausgedrückt, sind rvalue Referenzen asymmetrische:

  • sie nur rvalues ​​erhalten, dh entweder temporäre Objekte, weg Objekte zu gehen, oder Objekte, die aussehen, als ob sie rvalues ​​sind (zB das Ergebnis von std::move(o))
  • die rvalue Referenz selbst sieht jedoch wie ein L-Wert
8

bar ist eigentlich ein lvalue, also müssen Sie es durch std::move übergeben, so dass es als ein rvalue im Aufruf an push_back gesehen wird.

Die Überlastung Foo::AddBar(UniqueBar&& bar) stellt einfach sicher, dass diese Überlastung ausgewählt wird, wenn ein Rvalue in einem Aufruf an Foo::AddBar übergeben wird. Aber das bar Argument selbst hat einen Namen und ist ein Lvalue.

+0

auch wird die 'unique_ptr (const unique_ptr &)' gelöscht. – xis

9

Verwirrend wie es scheinen mag, ein rvalue Verweis bindet an ein rvalue, verwendet aber als ein Ausdruck ist ein lvalue.

3

bar ist als R-Wert-Referenz definiert, aber seine Wert-Kategorie ist ein Lvalue. Dies ist so, weil das Objekt einen Namen hat. Wenn es einen Namen hat, ist es ein Lvalue. Daher ist eine explizite std::move erforderlich, da der Name gelöscht werden soll und ein xvalue (eXpiring-rvalue) zurückgegeben werden soll.

Verwandte Themen