GSL Warnung "used after it was moved" in doppelt erbendem move contructor
-
Ich versuche meinen Code sicherer zu machen und habe dafür die GSL warnungen aktiviert. Ich verstehe warum im folgenden Code eine Warnung ausgelöst wird, aber nicht was ich tun kann um es besser zu machen?
class Base { public: Base() = default; Base(Base&& other) noexcept {} private: }; class Base2 { public: Base2() = default; Base2(Base2&& other) noexcept {} private: }; class Derived : public Base, public Base2 { public: Derived() = default; Derived(Derived&& other) noexcept : Base(std::move(other)), Base2(std::move(other)) {} // ^^^^ triggers: 'other' used after it was moved };Der Code funktioniert wie er soll, würde aber trotzdem gerne die Warnung nicht einfach nur deaktivieren.
Danke!
-
Das Ding kann ja nicht zu zwei verschiedenen Orten gemoved werden. Wie soll das logisch gehen? Dein Code tut nix, von daher ist es schwer zu sagen, was du logisch eigentlich möchtest. Du musst dir die Frage stellen, ob das Ding in Base 1 und Base 2 dasselbe oder das gleiche sein sollen.
Wenn das bei dir trotzdem funktioniert, dann entweder weil du so wie derzeit gar nichts mit dem Objekt tust, oder du hast bloß Pech, dass dein fehlerhafter Code nicht explodiert. Denn fehlerhaft ist das.
-
Danke für die Antwort. Der Code ist natürlich nur ein minimalisiertes Beispiel. In meiner Anwendung werden diverse Inhalte bewegt.
Ich hatte gelesen, dass mein Beispiel nicht crasht, weil jeweils nur die Teile bewegt werden, die nur die beerbte Klasse betreffen.
Deswegen die Frage: Wie kann ein move constructor korrekt alle move contructors aller beerbten Klassen durchlaufen ohne dass diese Warnung kommt? Oder kann man schlicht nur die Warnung ignorieren wenn sie im move contructor ausgelöst wird?
-
@mael15 sagte in GSL Warnung "used after it was moved" in doppelt erbendem move contructor:
Deswegen die Frage: Wie kann ein move constructor korrekt alle move contructors aller beerbten Klassen durchlaufen ohne dass diese Warnung kommt? Oder kann man schlicht nur die Warnung ignorieren wenn sie im move contructor ausgelöst wird?
Im Zweifel erstmal so:
Derived(Derived&& other) noexcept = default;oder gleich ganz weglassen.
Wenn es das Klassendesign tatsächlich erfordert, das Move explizit zu implementieren dann ist das vor allem davon abhängig, wie die Klassen konkret aussehen und ihre Ressourcen verwalten.
Ein generischer Ansatz könnte z.B. sein die Unterobjekte von
Derivedgetrennt zu moven:Derived(Derived&& other) noexcept : Base(std::move<Base>(other)), Base2(std::move<Base2>(other)) {}Das dürfte in etwa auch das sein, was der Compiler generiert, wenn man den Move-Konstruktor "defaulted" (Nachtrag: und für dein Beispiel dürfte das auch äquivalent zu dem sein, was du bereits da stehen hast).
Da würde ich aber genau drauf acht geben, dass die Unterobjekte keine gemeinsame Basisklasse haben (Diamond Problem), sonst wird es komplizierter. Bei nicht-virtueller Vererbung sollten die Basisobjekte separat existieren und obiger Ansatz ebenfalls funktionieren. Bei virtueller Vererbung müsste man aber sicherstellen, dass das Unterobjekt der gemeinsamen Basisklasse nur einmal verschoben wird. Da wüsste ich jetzt auf Anhieb auch nicht, wie man das generisch implementieren könnte, ohne die konkreten Klassen zu kennen.
Aber generell: lass Compiler das machen und Defaults generieren. Wenn deine Klasse keine Ressourcen selbst verwaltet, dann musst du auch keine Move-Konstruktoren implementieren. Mein Bauchgefühl sagt mir auch, dass wenn in der Klassenhierarchie so viele Move-Konstruktoren benötigt werden, dass es tatsächlich zu deinem aktuellen Problem kommt, dass man das sehr wahrscheinlich eher durch ein besseres Klassendesign lösen kann (oder noch besser - mehr Defaults!).
-
@SeppJ sagte in GSL Warnung "used after it was moved" in doppelt erbendem move contructor:
Das Ding kann ja nicht zu zwei verschiedenen Orten gemoved werden. Wie soll das logisch gehen? Dein Code tut nix, von daher ist es schwer zu sagen, was du logisch eigentlich möchtest. Du musst dir die Frage stellen, ob das Ding in Base 1 und Base 2 dasselbe oder das gleiche sein sollen.
Wenn man es ganz genau nimmt, dann werden mit den beiden Moves auch zwei verschiedene Unterobjekte des
Derived-Objekts gemoved, da die Move-Konstruktoren vonBaseundBase2über ihren Parameter dasDerived&&-Objekt implizit nachBase&&undBase2&&casten. Das könnte für viele Klassenhierarchien tatsächlich korrekt so sein. Für das triviale Beispiel hier auf jeden Fall.Mein erster Instinkt war auch "man kann nicht zweimal dasselbe moven". Aber wie immer gibts in C++ viele Nuancen
