eigenen Klassentyp als Template Parameter an einen Membertyp uebergeben..



  • Hallo,

    Ich habe da eine Frage zur Uebergabe des eigenen Klassen Typs als Template Parameter fuer den Typ einer Instanz eines Member. Ist sowas moeglich?

    Also zB sowas wie..

    template< typename Arg1, typename Arg2 >
    class Foobar
    {
    private:
      SomeClass< Foobar< Arg1, Arg2 > > member;
    //...
    

    Das erinnert irgendwie an Vererbung einer Basisklasse von ihrer eigenen abgeleiteten Klasse, das ist ja afair auch nicht moeglich, oder geht das doch irgendwie?

    ...dabei beisst sich doch da die Katze auch irgendwie in den eigenen Schwanz, wenn damit wieder die Argumente uebergeben werden muessen und somit wieder ein "SomeClass" Member erzeugt werden wuerde, der wieder.. etc etc Oder gibt es da irgendwelche Einschraenkungen?



  • naja du kannst SomeClass dann kein Member vom Typ seines Templateparameters geben, sonst macht der Compiler irgendwann die Grätsche. Aber möglich ists.
    Das gleiche problem entsteht natürlich wenn man auf die Weise eine "Ringableitung" macht:

    template <class T> class Daddy; //forward-decl.
    
    class B : public Daddy<B> 
    {};
    
    template <class T> 
    class Daddy : public T 
    {};
    

    Aber es steht ja nirgendwo geschrieben, dass eine Templateklasse ihre Templateparameter als Member oder als Elternklasse haben muss oder? Es gibt z.B. häufig die Vererbung von class B im Beispiel. Wenn ein Klassentemplate dazu designt wurde, dass andere Klassen von der Templateklasse erben, die die Kindklasse als Templateparameter hat, dann nennt man das auch "Curiously Recurring Template Pattern", ab und zu auch CRTP abgekürzt (zumindest hier im Forum)



  • Hmm, also ich hab das jetz mal versucht nachzubauen..

    Aber bei mir kompiliert das nicht?! Seltsamerweise compiliert es, wenn ich statt zB "Derived" eine Dummy class implementiere und diese per Template Parameter uebergebe, also etwa so:

    class Dummy[];
    

    Hier also mein Code, versucht hab ich mit g++ 4.3.2:

    #include <iostream>
    
    // forward declaration
    template< class T > class Base;
    class Derived;
    
    // base class
    template< class T > 
    class Base 
      : public T
    {
    public:
      Base()
      {
        std::cout << "\t\tBase< T >::Base() - ctor\n";
      }
    };
    
    // Dummy
    // class Dummy{};
    
    // derived class
    class Derived 
      : public Base< Derived > // hier statt "Derived" Dummy
    {
    public:
      Derived()
      {
        std::cout << "\tDerived::Derived() - ctor\n";
      }
    };
    
    /*
      main...
    //*/
    int main()
    {
      using namespace std;
    
      cout << "instantiate..";
      Derived d;
      cout << endl;
    
      cout << "READY.\n";
      return 0;
    }
    

    Die Fehlermeldung ist folgende (Zeilennummern stimmen mit oben ueberein 😉 ):

    g++ -c -g -Wall crtp.cpp
    crtp.cpp: In instantiation of ‘Base<Derived>’:
    crtp.cpp:29: instantiated from here
    crtp.cpp:13: error: invalid use of incomplete type ‘class Derived’
    crtp.cpp:5: error: forward declaration of ‘class Derived’
    make: *** [crtp.o] Error 1

    ..bin jetzt etwas verwirrt, sollte es denn normalerweise kompilieren? Was waere denn mein Output? Wenn ich ueberlegen wuerde, sollte es doch genauso verboten sein Template-ring-abhaengigkeiten zu erzeugen, wie bei Vererbungen?! 😕



  • Du brauchst deine Klasse nicht vorwärts zu deklarieren (und darfst es offenbar auch nicht, ich hab das auf diese Art noch nie probiert, drum kenn ich da die genauen regeln nicht.) Die template-Klassen-Deklaration gibt dem Compiler nur ein Muster vor. Er macht daraus erst etwas anfassbares, wenn du da : public base< Derived > schreibst, aber an der Stelle ist der Typ Derived schon bekannt. Kein Problem also.



  • Ach in der Base leitest du wieder von der klasse Derived ab, dann. Das geht doch natürlich nicht. Derived leitet von Base< Derived > ab und Base< Derived > dann wieder von Derived. Irgendwie klingt das doch nach einem endlosen Kreis? 🙂



  • ich hab ja oben geschrieben, da macht der Compiler nicht mit 🙄



  • Hab's gerade ohne Forward Deklaration versucht - selber Fehler, nur diesmal meckert er das die "forward declaration" in der Zeile der Definition von Derived.. nicht ganz stimmt.

    Wie gesagt mit "Dummy" statt "Derived", kompiliert der Spass, es scheint irgendwie an der Template Geschichte und dem Verhaeltnis von Base und Derived zu liegen, nur das der Compiler das so nicht sagt. Kann das sein??!!! 😕

    Edit:

    pumuckl schrieb:

    ich hab ja oben geschrieben, da macht der Compiler nicht mit 🙄

    okok.. aber nja, sagen koennte er das schon irgendwie, da sucht ja jeder nach nem Fehler mit Forward Deklaration..



  • Das Vorwärtsdeklarieren war in diesem Fall gar kein Problem (In dem Fall, den ich zuerst angenommen hatte aber auch gar nicht nötig). Das Problem ist, dass der Compiler in eine Endlosschleife verfallen würde. Erwartest du, dass:

    class B;
    
    class A : public B {
    };
    
    class B : public A {
    };
    

    kompiliert?


Anmelden zum Antworten