pure virtual clone mit shared_ptr?
-
hallo zusamen,
gibt es eine möglichkeit das hinzubekommen?
ich bekomme
error C2555: "Child::cloneSharedPtr": Der überschreibende virtuelle Funktionsrückgabetyp unterscheidet sich und ist keine "covariant" von "Father::cloneSharedPtr"class Father { public: virtual std::shared_ptr<Father> cloneSharedPtr() = 0; }; class Mother { public: virtual std::shared_ptr<Mother> cloneSharedPtr() = 0; }; class Child : public Father, public Mother { public: std::shared_ptr<Child> cloneSharedPtr() override { return std::make_shared<Child>(*this); } };
-
Ich rieche ein Diamond problem.

Edit: Lies mal hier weiter: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43120
#include <memory> class Father { public: virtual std::shared_ptr<void> cloneSharedPtr() = 0; }; class Mother { public: virtual std::shared_ptr<void> cloneSharedPtr() = 0; }; class Child : public Father, public Mother { public: std::shared_ptr<void> cloneSharedPtr() override { return std::make_shared<Child>(*this); } };
-
Lustig finde ich den abschließenden Kommentar: gefixt für 4.6, aber danach wieder rückgängig gemacht.
-
Probiere mal:
#include <memory> #include <iostream> /// Basis-Klasse Father class Father { public: virtual ~Father() = default; /// Erzeugt eine Kopie als Father-Pointer in einem shared_ptr. /// Jede abgeleitete Klasse muss diese Methode überschreiben. virtual std::shared_ptr<Father> cloneAsFather() const = 0; }; /// Basis-Klasse Mother class Mother { public: virtual ~Mother() = default; /// Erzeugt eine Kopie als Mother-Pointer in einem shared_ptr. /// Jede abgeleitete Klasse muss diese Methode überschreiben. virtual std::shared_ptr<Mother> cloneAsMother() const = 0; }; /// Abgeleitete Klasse Child, erbt mehrfach von Father und Mother class Child : public Father, public Mother { public: int someData = 0; Child() = default; explicit Child(int d) : someData(d) {} /// Implementierung für das Klonen als Father /// Rückgabetyp passt exakt zu Father::cloneAsFather() std::shared_ptr<Father> cloneAsFather() const override { // std::make_shared<Child>(*this) erzeugt eine Kopie von *this (Child), // der shared_ptr<Child> wird implizit nach shared_ptr<Father> konvertiert. return std::make_shared<Child>(*this); } /// Implementierung für das Klonen als Mother /// Rückgabetyp passt exakt zu Mother::cloneAsMother() std::shared_ptr<Mother> cloneAsMother() const override { // Auch hier: Kopie von Child, diesmal als Mother-Interface zurückgegeben. return std::make_shared<Child>(*this); } }; int main() { Child original(42); // Über Father-Schnittstelle klonen std::shared_ptr<Father> f = original.cloneAsFather(); // Über Mother-Schnittstelle klonen std::shared_ptr<Mother> m = original.cloneAsMother(); // Wenn du wieder an Child ran möchtest: auto fAsChild = std::dynamic_pointer_cast<Child>(f); auto mAsChild = std::dynamic_pointer_cast<Child>(m); if (fAsChild && mAsChild) { std::cout << "fAsChild->someData = " << fAsChild->someData << "\n"; std::cout << "mAsChild->someData = " << mAsChild->someData << "\n"; } }
-
@Erhard-Henkes
MS schreibt dazu, man solle den Rückgabetyp invoidändern: https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2555?view=msvc-170Problematisch finde ich, dass du die Semantik/Schnittstelle geändert hast...
cloneSharedPtr()wird nun nicht mehr in der gemeinsamen Kindklasse überschrieben.
-
@Lennox sagte in pure virtual clone mit shared_ptr?:
Ich rieche ein Diamond problem.

Ja, dachte ich auch schon, komisch ist nur, dass das ganze ohne shared_ptr funktioniert:
class Father { public: virtual Father* clone() = 0; }; class Mother { public: virtual Mother* clone() = 0; }; class Child : public Father, public Mother { public: Child* clone() override { return new Child(*this); } };Ich würde aber gerne auf smart pointer umsteigen.
Das ganze mit cloneAsFather und cloneAsMother zu trennen funktiert und wäre eine gute Notlösung, macht aber die elegante vielfache Vererbung hinfällig? In meinem konkreten Anwendungsfall habe ich auch noch mehrere weitere Ebenen: Grandfather, Unkle etc...
-
@Lennox Für das Diamon Problem müsste es aber noch eine gemeinsame Baseklasse von
FatherundMothergeben, die eine Funktion implementiert, die vonChildaufgerufen wird. Das Problem jedoch ist, dass eine overriding Funktion sich nicht ausschließlich im Rückgabetyp von der Basisfunktion unterscheiden darf, mit der Ausnahme, von Pointern bei Vererbung. Aus @Erhard-Henkes Link dazu:There's an exception to this rule for certain return types. When a derived class overrides a public base class, it may return a pointer or reference to the derived class instead of a base-class pointer or reference. These return types are called covariant, because they vary together with the type. This rule exception doesn't allow covariant reference-to-pointer or pointer-to-pointer types.
Man kann mit einer gemeinsamen Basisklasse Arbeiten, hat dann aber wirklich das Diamond Problem mit allen eventuellen Problemen.
z.B. sowas
#include <iostream> #include <memory> class Human { public: std::string type; }; class Father : public virtual Human { public: virtual std::shared_ptr<Human> cloneSharedPtr() = 0; }; class Mother : public virtual Human { public: virtual std::shared_ptr<Human> cloneSharedPtr() = 0; }; class Child : public Father, public Mother { public: std::shared_ptr<Human> cloneSharedPtr() override { return std::static_pointer_cast<Human>(std::make_shared<Child>(*this)); } }; int main() { Child a; a.type = "Child1"; auto b = a.cloneSharedPtr(); std::cout << b->type << "\n"; a.type = "changed"; std::cout << a.type << "\n"; std::cout << b->type << "\n"; }Aber, man kann dann nicht einfach von "Human" auf "Child" casten.
Das ganze sieht sehr nach einem "akademischen" Beispiel aus. Normalerweise würde ich sagen: Mehrfachvererbung meiden. In Java und in C# ist das z.B. auch gar nicht erlaubt und sollte auch in C++ nur mit gutem Grund benutzt werden.
Der nächste Schritt wäre dann, vlt ganz auf Vererbung zu verzichten

@mael15 Nutz doch bitte Code Tags, das macht deine Posts lesbarer. Einfach Code markieren und auf </> neben dem Programmiersprache Drop Down klicken. Du kannst deine Posts auch bearbeiten.
-
@Schlangenmensch Vielen Dank für die Erklärung!
(aber den Link hatte ich gepostet...)@Erhard-Henkes Auch dir ein Danke.