std::move und setter
-
Hi,
geht um ein GUI System mit verschiedenen Controls und den entsprechenden Gettern/Settern. Dort experimentiere ich momentan mit std::move bei Settern:
class Color { public: Color() { std::cout << "Color()\n"; } Color(const Color ©) { std::cout << "Color(const&)\n"; } Color(Color &&m) { std::cout << "Color(&&)\n"; } ~Color() { std::cout << "~Color()\n"; } Color& operator=(Color const& rhs) { std::cout << "Color=(const&)\n"; return *this; } Color& operator=(Color && rhs) { std::cout << "Color=(&&)\n"; return *this; } }; class Control { public: virtual void SetColor(Color color) { this->color = std::move(color); } private: Color color; }; class Label : public Control { }; class Button : public Control { public: virtual void SetColor(Color color) { label.SetColor(color); Control::SetColor(std::move(color)); } private: Label label; };SetColor bekommt Color per Value, da ich eine Kopie mache und der Compiler das ja für mich übernehmen könnte. Wenn ich den Code laufen lasse bekomme ich bei einem Button::SetColor als Ausgabe
int main() { Button b; Color c; b.SetColor(std::move(c)); }Color(&&) Color(const&) Color=(&&) ~Color() Color(&&) Color=(&&) ~Color() ~Color()während ich bei der Version mit SetColor(const Color &color)
Color=(const&) Color=(const&)erhalte.
Seh ich das richtig, dass es sich also nicht generell anbietet (gerade mit solchen Vererbungsketten) auf byvalue und move zu bauen?
Bei einem Setter für einen billig zu movenden Datentyp sieht es nämlich gleich etwas anders aus. Statt SetColor nun SetData(std::vector<Color> data).
Hier erzeugt der byvalue Code data.size() Kopien von Color, während die const& Version 2*data.size() Kopien erzeugen muss.Das ist irgendwie unbefriedigend, da man beim Schreiben wissen muss, ob ein Typ einfach zu moven ist oder nicht, um den entsprechenden Setter anzubieten.
Generell sind die ganzen Setter nicht performancekritisch, sodass ich der Konsistenz wegen zu const& überall tendieren würde.Wie gestaltet ihr sowas?
-
Seh ich das richtig, dass es sich also nicht generell anbietet (gerade mit solchen Vererbungsketten) auf byvalue und move zu bauen?
Ich verstehe nicht, wie du den Schluss aus deinem Beispiel ziehst. Wenn die Vektoren groß sind, und Moves relativ zu Kopien nichts kosten, dann hast du damit die Laufzeit des Code-Schnipsels halbiert (falls genügend viele Funktionsargumente tatsächlich gemoved werden können!).
Ein Problem besteht darin, den Move beim Aufrufen des Setters auch konsequent zu nutzen. Wie willst du dem User sagen, dass er Moven sollte, wo möglich? Oder nutzt nur du das (und selbst dann wird es möglicherweise problematisch, sobald du die Funktion oder die Parameter veränderst).
Edit: Ich habe den Post nicht ganz zu ende gelesen...
Generell sind die ganzen Setter nicht performancekritisch, sodass ich der Konsistenz wegen zu const& überall tendieren würde.
Nun, um die Setter geht es nicht, sondern um den Datentyp der kopiert/gemoved werden soll. Sind es große Arrays o.ä.?
Das ist irgendwie unbefriedigend, da man beim Schreiben wissen muss, ob ein Typ einfach zu moven ist oder nicht, um den entsprechenden Setter anzubieten.
Dann wirst du dich auf eine Konvention festlegen müssen, die natürlich eine Const-Referenz sein wird. Das heißt, die Diskussion bringt hier nichts, da es sowieso Overkill bedeutet, Setter mit verschiedenen Übergabe-Methoden bereitzustellen, für Typen, die du nicht genau kennst (letzteres ist auch merkwürdig, warum weißt du nicht, ob die Typen schnell movebar sind?).
Man sollte so etwas auf keinen Fall für Objekte machen, bei denen der Laufzeitunterschied von Move und Kopie nicht so weit auseinanderliegt. Da kommt nämlich der Overhead von den "Zwischenobjekten" dazu, und das kostet moglicherweise mehr.
Aber das weißt du natürlich alles schon.