Anfänger std::move
-
Tachyon, der von Dir verlinkte Artikel ist leider nicht mehr aktuell. Es gab seitdem zwei wichtige Änderungen bei den Rvalue-Referenz-Regeln. Heute verweist eine frisch initialisierte non-const Rvalue-Referenz immer nur auf ein Objekt, was verändert werden darf, ohne dass es jemanden stören würde. Anfang 2009 konnte man aber noch schreiben
int i = 0; int && rr = i;Das kompiliert jetzt aber nicht mehr. Man hat aus Sicherheitsgründen die Regeln abgeändert, weil es sonst zu versehentlichen Modifikationen von anderen Objekten hätte kommen können.
Wer gesprochenes amerikanisches Englisch versteht, kann sich ja mal Stephan T. Lavavej's Vortrag anhören.
-
Hallo,
ich habe mal in einer Klasse den Move-Copy-Ctor und den Move Assignment Operator implementiert. Falls etwas besser gemacht werden könnte, bitte sagen.#include <iostream> #include <vector> #include <string> class X { private: std::vector<std::string> * m_pResource; public: X():m_pResource(new std::vector<std::string>) {} ~X() { delete m_pResource; m_pResource = nullptr;} X(X const& other); //normal copy ctor X(X&& tmp); //move copy ctor X& operator= (X const& rhs); //normal assignment operator X& operator=(X&& tmp); //Move assignment operator }; X::X(X const& other) { std::cout << "Copy Ctor\n"; m_pResource = new std::vector<std::string>(*other.m_pResource); //teuer } X::X(X&& tmp) { std::cout << "Move Copy Ctor\n"; m_pResource = nullptr; std::swap(m_pResource,tmp.m_pResource); } X& X::operator=(X&& tmp) { std::cout << "Move Assignmnet Operator\n"; std::swap(m_pResource,tmp.m_pResource); return *this; } X& X::operator= (X const& rhs) { if (this != &rhs) { std::cout << "Assignment operator\n"; std::vector<std::string> * tmp = new std::vector<std::string>(*rhs.m_pResource); //teuer std::swap(m_pResource,tmp); } return *this; } X foo() { X x; return x; } int main() { X x; std::cout << "x = foo(); \n"; x = foo(); //Ohne Move Ctor und ohne Move Assignment operator wird zuerst der Copy-Ctor aufgerufen und anschließend der Assignment operator aufgerufen (ohne NRVO) //in beiden Fällen wird die Resource teuer kopiert //mit NRVO: nur der Assignment operator wird aufgerufen //mit Move Copy-Ctor und Move Assignmnet operator wird zuerst der MoveCopy-Ctor und anschließend der Move Assignment operator aufgerufen (ohne NRVO) //in beiden Fällen werden nur Pointer umgebogen //mit NRVO: nur der Move Assignment operator wird aufgerufen std::cout << "X y = x \n"; X y =x; //x ist kein rvlaue -> copy ctor std::cout << "X v; v = x;\n"; X v; v = x; //x ist kein rvlaue -> assignment operator std::cout << "X z;z = X();\n"; X z; z = X(); // X() ist ein Rvalue -> move assignment operator std::cout << "X t = std::move(z);\n"; X t = std::move(z);// z wird zum Rvalue -> move copy ctor (auf z sollte nicht mehr zugegriffen werden) std::cout << "X c;c = std::move(t);\n"; X c; c = std::move(t); //t wird zum Rvalue -> move assignment operator (auf t sollte nicht mehr zugegriffen werden) return 0; }
-
einwurf schrieb:
Falls etwas besser gemacht werden könnte, bitte sagen.
Der kopierende Zuweisungsoperator führt etwas aus, was du bereits implementiert hast (und auch noch falsch, weil du den Speicher nicht freigibst). Zweckmäßigerweise sollte er eher so aussehen (Copy+Move, statt wie bisher Copy+Swap):
X& X::operator= (X const& rhs) { std::cout << "Assignment operator\n"; return *this = X(rhs); }Außerdem prüfst du beim Kopieren nicht, ob evtl. das Argument bereits leer ist.
-
[quote="camper"]
einwurf schrieb:
X& X::operator= (X const& rhs) { return *this = X(rhs); }Was wird denn hier zurückgegeben? Also was ist der Rückgabewert einer zuweisung?
-
@camper, stimmt. Eine Speicherlücke.
Es geht aber noch einfacher (nur ein operator=), wenn ich nicht wieder etwas übersehen habe.class X { private: std::vector<std::string> * m_pResource; public: X():m_pResource(new std::vector<std::string>) {} ~X() { delete m_pResource; m_pResource = nullptr;} X(X const& other); //normal copy ctor X(X&& tmp); //move copy ctor X& operator= (X rhs); //Move assignment operator and normal assignment operator }; X::X(X const& other) { std::cout << "Copy Ctor\n"; m_pResource = new std::vector<std::string>(*other.m_pResource); //teuer } X::X(X&& tmp) { std::cout << "Move Copy Ctor\n"; m_pResource = nullptr; std::swap(m_pResource,tmp.m_pResource); } X& X::operator= (X rhs) { std::cout << "Move and normal assignment operator\n"; if(this != &rhs) { std::swap(m_pResource, rhs.m_pResource); } return *this; }
-
Wenn ich auch mal einen Einwurf machen dürfte: Die Dinger heißen copy constructor, copy assignment operator, move constructor und move assignment operator. Insbesondere "Move-Copy-Ctor" ergibt keinen Sinn.
-
@Bashar, danke. Ich dachte irrtümlich, weil der Copy-Ctor überladen wird heißt das nun ...
Das if(this != &rhs) kann man sich auch sparen, da "rhs" ja immer ein anderes Objekt als "this" ist.
Das swappen für den Fall x=x; sollte keine Probleme machen, oder?X& X::operator= (X rhs) { std::cout << "Move and normal assignment operator\n"; std::swap(m_pResource, rhs.m_pResource); return *this; }
-
einwurf schrieb:
@camper, stimmt. Eine Speicherlücke.
Es geht aber noch einfacher (nur ein operator=), wenn ich nicht wieder etwas übersehen habe....Das ist so möglich, aber im Movefall führst du so u.U. 2 Moves aus.
-
Fraaaggger schrieb:
camper schrieb:
X& X::operator= (X const& rhs) { return *this = X(rhs); }Was wird denn hier zurückgegeben? Also was ist der Rückgabewert einer zuweisung?
X(rhs) erzeugt ein temporäres Objekt, das dann für die Zuweisung genutzt wird. Weil es sich um ein rvalue handelt, wird die andere Zuweisungsüberladung aufgerufen, und somit das zurückgegeben, was die andere Funktion zurückgibt, also *this.
-
camper schrieb:
Fraaaggger schrieb:
camper schrieb:
X& X::operator= (X const& rhs) { return *this = X(rhs); }Was wird denn hier zurückgegeben? Also was ist der Rückgabewert einer zuweisung?
X(rhs) erzeugt ein temporäres Objekt, das dann für die Zuweisung genutzt wird. Weil es sich um ein rvalue handelt, wird die andere Zuweisungsüberladung aufgerufen, und somit das zurückgegeben, was die andere Funktion zurückgibt, also *this.
Ahh! Jetzt machts sinn, danke!
