Templates für Fortgeschrittene



  • Hi,
    ich habe eine Klasse Base:

    class Base
    {
    public:
      virtual Base *createDerived()
      {
        return 0;
      }
    };
    

    und eine davon abgeleitete Template Klasse BaseT:

    template <class T>
    class BaseT : public Base
    {
    public:
      virtual Base *createDerived()
      {
        return new DerivedT<T>();  //nicht schön, da Wissen von Ableitung benötigt
      }
    };
    

    und eine weitere davon abgeleitete Template Klasse DerivedT:

    template <class T>
    class DerivedT : public BaseT<T>
    {
    
    };
    

    So weit so gut. Angenommen ich habe eine Objekt:

    Base *base = new BaseT<int>;
    

    Dann möchte ich etwas haben wie:

    Base *derived = base->createDerived();
    

    Das heißt, ein Objekt vom Typ DerivedT, dass den selben Template-Typen hat wie mein "base" Objekt.

    Mein Ansatz funktioniert zwar, ist aber nicht besonders schön (siehe Kommentar).

    Mein bevorzugter Ansatz wäre eine Fabrik:

    template <class T>
    class Factory
    {
    public:
      static Base *createDerived()
      {
        return new DerivedT<T>();
      }
    };
    

    Und jetzt fangen die Probleme an:
    Wie sage ich der Factory, welchen Typ sie verwenden soll. Es soll ja derselbe typ sein wie der von "base".

    Base *derived2 = Factory<sameAsBase>::createDerived();
    

    Und da stößt C++ an seine Grenzen. Kennt jemand einen Ausweg?



  • Edit: //War quatsch



  • Warum machst du aus der Factory-Klasse nicht eine Factory-Methode?

    template <typename T>
    Base* createDreived()
    {
        return new DerivedT<T>();
    }
    
    //-->>
    Base* b = createDerived<int>();
    


  • Und was hilft mir das? Das Problem bleibt doch das gleiche, oder?

    Base* b = createDerived<int>();
    

    Das Oroblem ist, ich weiß nicht welchen Typ ich übergeben muss.
    Der Typ soll ja der gleiche sein wie aus "base":

    Base *base = new BaseT<int>;
    

    Angenommen ich habe eine Funktion, die nicht weiß wie base erstellt wurde:

    void test(Base *base)
    {
    Base* derived = createDerived<type von base>();	
    }
    

    Hm... 😞



  • Dann muss deine Klasse selber auch ne Factory sein. Denn den Typen extrahieren, um ihn als template-Parameter zu übergeben, funktioniert nicht.



  • Spendier Deinen Klassen eine clone() Methode, die jeweils eine neue Instanz der Klasse zurückliefert. Sowas wie:

    class Base
    {
       ...
       Base * clone() = 0;
    }
    
    class DerivedA : public Base
    {
       Base * clone() {return new DerivedA;}
    }
    
    class DerivedB : public Base
    {
       Base * clone() {return new DerivedB;}
    }
    


  • Im prinzin ne gute Idee aber:

    class DerivedA : public Base
    {
       Base * clone() {return new DerivedA;}
    }
    

    Die clone von DerivedA soll ja eine Instanz von DerivedB erstellen. Und somit bin ich wieder am Anfang, denn DerivedA soll DerivedB möglichst nicht kennen.



  • diavo schrieb:

    Die clone von DerivedA soll ja eine Instanz von DerivedB erstellen. Und somit bin ich wieder am Anfang, denn DerivedA soll DerivedB möglichst nicht kennen.

    Es wird immer spannender! Wieso soll jetzt DerivedA eine Instanz von DerivedB erstellen? Und was hat das mit den Templates zu tun? Und, klar, wenn eine Methode etwas mit einem Type anstellen muss, dann muss natürlich der Type bekannt sein.



  • Ok, hier ein Beispiel das zeigt dass es geht.

    struct Base
    {
        virtual Base* create()=0;
    };
    
    template <typename T>
    struct DerivedB;
    
    template <typename T>
    struct DerivedA : public Base
    {
        T val;
        DerivedA* create() {
            return new DerivedA;
        }
        DerivedB<T>* createB();
    };
    
    template <typename T>
    struct DerivedB : public Base
    {
        T val;
        DerivedB* create() {
            return new DerivedB;
        }
    };
    
    template <typename T>
    DerivedB<T>*
    DerivedA<T>::createB() {
        return new DerivedB<T>;
    }
    
    int main()
    {
        DerivedA<int> da1;
        Base* b1 = da1.createB();
        Base* b2 = b1->create();
    
        DerivedB<int>* db1 = dynamic_cast<DerivedB<int>*>(b2);
        DerivedB<int>* d2 = db1->create();
    }
    

    Und bitte sag konkret, was dir nicht passt und vor allem die endgültige Vorgabe, was dein Konstrukt können muss. Sonst geht das hier noch ewig 😉



  • Hi,
    also das letzte Beispiel

    template <typename T>
    struct DerivedA : public Base
    {
        T val;
        DerivedA* create() {
            return new DerivedA;
        }
        DerivedB<T>* createB();
    };
    

    ist kein gutes Design, weil ja DerivedA die abgeleitete Klasse DerivedB kennen muss. Das war ja auch mein 1. Vorschlag.
    Ich möchte also eine Möglichkeit genau das zu tun, was dieses Beispiel tut, aber eben ohne die zirkuläre Abhängkeit von DerivedA und DerivedB.



  • in dem Beispiel ist nichts zirkulär.

    natürlich wird das mit einer Factory nicht gehen, da du dafür den Typ brauchst, wie du bereits erkannt hast. Da dein Abstraktionsmodell ganz klar sagt: "Ich werfe den Typen weg" wirst du um eine Factorymethode nicht herumkommen.

    Einzige gangbare Alternative: die Entität, die dir dein BaseT erzeugt, muss dann auch dein Derived erzeugen.



  • Ok. Das hatte ich schon vermutet. Danke


Anmelden zum Antworten