Move/Copy Constructor/Operator with const class members
-
Hi,
ich habe gerade versucht zum ersten Mal in meinem Leben einen eigenen Move-Constructor zu schreiben und bin auf einige Hindernisse gestoßen.Meine Klasse bsteht nur aus Const Variablen und ich habe einige Probleme den move und assignment Operator zu implementieren und ich bin mir nicht sicher ob meine Konstruktoren korrekt implementiert sind.
Ich habe auch noch einige Fragen:
1. Ich weiß, dass der Compiler automatisch Ctor/Dtor und Assignment Operator erstellt. Erstellt der Compiler auch automatisch MoveTor und Move-Operator?(VS2013)
Mit anderen Worten, muss ich bei jeder meiner Klassen einen eigenen Move-Operator erstellen um in STL-Containern von der Move-Operation zu profitieren?2. Reichen der STL Move/CTor um mit den Objekten umzugehen oder brauche ich zwingend auch beide Operatoren?
3. Ist meine Implementation von MoveTor und Ctor korrekt?
4. WIe implementiere ich Assignment/Move Operator, wenn einer oder alle Datenmember const sind.Beispiel Code:
P.S.: Nicht über den nackten Pointer wundern.
Der Pointer zeigt auf einen Speicherplatz einer Ressource die von einem unique_ptr gemanaged wird.
Die Lebenszeit des Resolve_obj ist per Design kürzer als die Lebenszeit des unique_ptr.class Resolve_obj{ public: const std::wstring caption; const std::wstring key; const int line_nr; const std::wstring * const filepath; Resolve_obj(const std::wstring && c_ption, const std::wstring && noti_key, const int line_num, const std::wstring * const filepth) : caption(c_ption), key(noti_key), line_nr(line_num), filepath(filepth) {} ~Resolve_obj(){} // Move constructor. Resolve_obj(Resolve_obj&& other) : caption(other.caption), key(other.key), line_nr(other.line_nr), filepath(other.filepath) {} // copy constructor Resolve_obj(Resolve_obj & other): caption(other.caption), key(other.key), line_nr(other.line_nr), filepath(other.filepath) {} // mein Verusch einen Move Operator zu implementieren // schlägt bei Variable caption und key fehl Resolve_obj& operator=(Resolve_obj&& other) { if (this != &other) { caption && other.caption; key && other.key; line_nr && other.line_nr; filepath && other.filepath; } return *this; } };
-
Erstellt der Compiler auch automatisch MoveTor und Move-Operator?
Das hängt von verschiedenen Kriterien ab.
Für den Konstruktor:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.
[...]
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
— a variant member with a non-trivial corresponding constructor and X is a union-like class,
— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor____________________________________________________________________________________________________________________________________________________
Für den Zuweisungsoperator:If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared move constructor,
— X does not have a user-declared copy assignment operator, and
— X does not have a user-declared destructor.
[...]
A defaulted copy/move assignment operator for class X is defined as deleted if X has:
— a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or
— a non-static data member of const non-class type (or array thereof), or
— a non-static data member of reference type, or
— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator, or
— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator.____________________________________________________________________________________________________________________________________________________
3. Ist meine Implementation von MoveTor und Ctor korrekt?
Sie wird funktionieren, aber intern movest du ja gar nichts, sondern kopierst!
Mach lieber folgendes draus:Resolve_obj(Resolve_obj&&) = default;
Das definiert ihn dir automatisch (falls möglich; s.o.)
WIe implementiere ich Assignment/Move Operator, wenn einer oder alle Datenmember const sind.
Kannste nicht.
const
-Objekte können (logischerweise) nicht gemoved werden, da sie eben unveränderlich sein sollen (const
ant) - das ginge nur wenn der Move-Assignment Operator dieser Objekttypen auch eineconst
-Referenz nimmt (was beistd::wstring
natürlich nicht gegeben ist) und das macht nur äußerst selten Sinn.caption && other.caption;
Das kompiliert nicht; Unsinn ist es allemal. Du möchtest wohl eher sowas
caption = std::move(other.caption);
Auch das wird nicht funktionieren; s.o.
-
Arcoth schrieb:
Sie wird funktionieren, aber intern movest du ja gar nichts, sondern kopierst!
Mach lieber folgendes draus:Resolve_obj(Resolve_obj&&) = default;
Das definiert ihn dir automatisch (falls möglich; s.o.)
Erstmal vielen Dank, das hat mir sehr geholfen.
Ich habe aber ein Problem mit oben genannter Zeile die funktioniert bei mir nicht auch, wenn ich überall const wegnehme.
Der Compiler meckert: "Ungültiger Typ für Standardkonstruktor". Könntest du dazu bitte noch ein wenig mehr Kontext schreiben?
Ich kriege es nicht zum laufen.Die Beispiele für den Move-Konstruktor habe ich aus der MSDN interpretiert: http://msdn.microsoft.com/de-de/library/dd293665.aspx
Aber für mich sah es da auch schon aus als ob nichts gemoved wird.
Wie sieht denn ein examplarischer ausgeschriebener Move-Konstruktor/Operator aus.
Die Beispiele im Netz sehen so aus wie die von der MSDN aber da wird doch eigentlich der Assignment Operator aufgerufen. Also kopiert und nicht gemoved, oder bin ich gedanklich aufs falsche Gleis gerutscht?
e.g.class Foo { private: int a; string b; public: Foo(int a, string b, int c, string d); ~Foo(); int c; string d; };
-
Könntest du dazu bitte noch ein wenig mehr Kontext schreiben?
Kann es sein dass VC++ 2013 das nicht unterstützt? Ist eine relativ nichtssagende Fehlermeldung, die nur darauf schließen lässt, dass VC++ Move-Konstruktoren nicht unterstützt (was aber selbst bei diesem lausigen Ding merkwürdig erscheint).
Wie sieht denn ein examplarischer ausgeschriebener Move-Konstruktor/Operator aus.
Ein Move-Konstruktor kopiert die Ressourcen nicht sondern nimmt nur die Verweise darauf über (also Zeiger/Referenzen). Ersteres nennt man deep copy (tiefe Kopie), letzteres shallow copy (oberflächliche Kopie).
Damit aber der Move-Konstruktor von der Overload-Resolution gefunden werden kann hat man sich rvalue-Referenzen ausgedacht und die Wertkategorien von Ausdrücken ergänzt. Damit ein Move-Konstruktor ausgewählt wird muss der Ausdruck Wertkategorie xvalue oder prvalue haben; zusammengefasst rvalue. Damit letzteres der Fall ist musst du den Ausdruck manchmal entsprechend Casten, vor allem immer wenn der Ausdruck einfach ein Name ist:
std::string a = "Hallo", b; b = a; // Hat kopiert b = static_cast<std::string&&>(a); // Der Ausdruck auf der rechten Seite ist nun ein rvalue (genauer: xvalue) und daher wird gemoved. // Statt das unterliegende Array zu kopieren wird einfach der Verweis darauf (hier höchstwahrscheinlich ein Zeiger) kopiert, und im moved-from Objekt irgendein gültiger State gesetzt... // Dieser State ist meistens einfach "leer". Kannst es durch ausgeben ausprobieren. b = std::move(a); // Diese nette Funktion kapselt einfach obigen Cast.
std::move
ist eine essentielle Funktion in C++11.
Wie sieht nun ein solcher Move-Konstruktor aus:#include <ostream> #include <stdexcept> // runtime_error class A { int* ptr; public: A() : ptr(nullptr) {} A(int i) : ptr(new int(i)) {} // Kopier-Konstruktor und -Zuweisungsoperator werden nicht generiert, // da ihre Move-Gegenstücke vorhanden sind. A(A&& a) : ptr(a.ptr) { a.ptr = nullptr; } A& operator=(A&& a) { if (&a != this) { delete ptr; // Aktuelles objekt zerstören ptr = a.ptr; // Neues annehmen a.ptr = nullptr; // Alten Verweis entfernen } return *this; } ~A() { delete ptr; } int value() const { if (!ptr) throw std::runtime_error{"Kuhle Fehlermeldung"}; return *ptr; } bool has_value() const {return ptr != nullptr;} }; std::ostream& operator<<(std::ostream& os, A const& a) { if (a.has_value()) os << a.value(); else os << "No value"; return os; } #include <utility> // move #include <iostream> // cout int main() { A a(3), b; std::cout << "a: " << a << " b: " << b << '\n'; b = std::move(a); std::cout << "a: " << a << " b: " << b << '\n'; // b = a; // Compilerfehler mit nützlicher Erklärung (von Clang zumindest) }
-
Arcoth schrieb:
Kann es sein dass VC++ 2013 das nicht unterstützt? Ist eine relativ nichtssagende Fehlermeldung, die nur darauf schließen lässt, dass VC++ Move-Konstruktoren nicht unterstützt (was aber selbst bei diesem lausigen Ding merkwürdig erscheint).
Danke für das Beispiel.
Ich grab mich gerade durch die VS2013 specs und irgendwie hätte ich erwartet, dass die Unterstützung irgendwo leicht verständlich steht.
Nach einer halben Stunde bin ich auf das gestoßen.
Visual Studio does not support defaulted move constructors or move-assignment operators as the C++11 standard mandates. For more information, see the Defaulted and Deleted functions section of Support For C++11 Features (Modern C++).
Ich glaube move Konstruktoren werden unterstützt aber keine Default Move Konstruktoren.
Das würde bedeuten ich muss die immer in jeder Klasse explizit hinschreiben.
Aber irgendwie finde ich das komisch, sie unterstützen rvalue referenzen und std::move aber sie erstellen keine Default Move Konstruktoren.
Das versteh ich nicht.
-
Move macht doch absolut keinen Sinn wenn deine Member const sind.
-
Kellerautomat schrieb:
Move macht doch absolut keinen Sinn wenn deine Member const sind.
const
-Member selbst machen keinen (bzw. nur selten) Sinn. U.a. Auch wegen sowas.