Alternative zu dynamic_cast



  • Hallo Forum,

    zunächst ein Code-Schnipsel:

    Base* pBase = GetBasePtr(); // raw-pointer auf Basisklasse
    std::unique_ptr<Base> uniqueBase = std::make_unique<???>(*pBase);
    

    Ich habe einen Basisklassenzeiger, von dem ich mir eine tiefe Kopie als unique_ptr erzeugen möchte. Ich möchte also die drei Fragezeichen oben durch den (abgeleiteten) Typ ersetzen, auf den pBase zeigt und mir ist nicht klar wie ich das am elegantesten hinbekomme.
    Ich könnte natürlich mit einer Reihe von dynamic_casts prüfen, auf welchen Typ pBase tatsächlich zeigt, das finde ich aber alles andere als elegant.

    Ich dachte an etwas in der Art:

    std::unique_ptr<Base> pUniqueBase = std::make_unique<decltype(*pBase)>(*pBase);
    

    Für einen Tipp wäre ich dankbar!



  • Eine Möglichkeit ist, eine clone-Funktion einzubauen.

    Du hast dann

    #include <iostream>
    #include <memory>
    
    class Base {
    public:
       virtual std::unique_ptr<Base> clone() const { return std::make_unique<Base>( *this ); }
       virtual void whoami() { std::cout << "Base\n"; }
    };
    
    class Derived : public Base {
    
    public:
       std::unique_ptr<Base> clone() const override { return std::make_unique<Derived>(*this); }
       void whoami() override { std::cout << "Derived\n"; }
    };
    
    int main() {
        std::unique_ptr<Base> b = std::make_unique<Base>();
        auto bc = b->clone();
        bc->whoami();
    
        std::unique_ptr<Base> d = std::make_unique<Derived>();
        auto dc = d->clone();
        dc->whoami();
    }
    

    Oder so ähnlich.



  • Danke für die Antwort!

    Nach ein wenig Recherche ist mir klar geworden, dass decltype eine rein statische Sache ist und nicht den Typ zur Laufzeit liefern kann. Das erklärt im Nachhinein auch die Compiler-Fehlermeldung, die ich bekommen habe.

    Cloning scheint für den Fall das Mittel der Wahl zu sein, allerdings hilft das natürlich nur dann, wenn man auch an die Basisklasse rankommt.
    Alternativ verschiedene dynamic_casts durchzuprobieren ist natürlich alles andere als schön.

    Irgendwie erstaunt mich ein wenig, dass es da keine elegantere Lösung gibt, aber ich werde es wohl akzeptieren müssen.



  • Es reicht wenn du an alle Klassen rankommst die konkret instanziert werden.
    Diese leitest du dann z.B. einfach alle von Clonable ab.

    Dann kannst du so klonen

    class Cloneable
    {
    public:
        virtual ~Cloneable() {}
        virtual Cloneable* clone() const = 0;
    };
    
    template <class T>
    T* clone(T const* o)
    {
        std::unique_ptr<Cloneable> c(dynamic_cast<Cloneable const&>(*o).clone());
        T* ct = &dynamic_cast<T&>(*c);
        c.release();
        return ct;
    }
    

    Das Ableiten von Cloneable und die Implementierung von Cloneable::clone kannst du dabei einfach über ein Template machen, z.B. mittels CRTP.



  • CRTP kannte ich noch nicht, vielen Dank für den Hinweis!

    Wirklich "schön" finde ich diese Lösung zwar auch nicht :), aber sie funktioniert für den von mir geschilderten Fall.


Log in to reply