Templatespezialisierung und abgeleitete Klassen



  • Hallo,

    wenn ich folgendes mache:

    class A{};
    
    class B: public A {};
    
    template<typename T> void foo(T t)
    {
      cout<<"Unspezialisiert"<<endl;
    }
    
    template<> void foo(A a){
      cout<<"Auf A spezialisiert"<<endl;
    }
    
    int main()
    {
      B b;
      foo(b);
    }
    

    Dann wird die unspezialisierte Version von foo aufgerufen. Ich hätte gerne, dass automatisch die spezialisierteste Version des Templates verwendet wird, schließlich ist B ein A. Gibt es da irgendeine Programmierstruktur die dies leisten kann? Den Fall von Mehrfachvererbung kann ich ausschließen, wäre natürlich schick, wenn es für den Fall auch ginge.

    Ich meine dabei jetzt nicht, foo<A>(b) zu benutzen. Im Beispiel geht das noch, aber bei komplizierteren Strukturen müsste ich dann immer Nachgucken, für welche der Basisklassen foo das spezialisierteste Template besitzt.


  • Mod

    Es ist zwar nicht genau das, was du suchst, aber man kann mit Operatorüberladung und einer gemeinsamen Basisklasse etwas ähnliches machen:

    class Base{};
    
    class A: public Base {};
    
    class B: public A{};
    
    class C: public Base{};
    
    void foo(A a){
      cout<<"Auf A spezialisiert"<<endl;
    }
    
    void foo(Base t)
    {
      cout<<"Unspezialisiert"<<endl;
    }
    
    int main()
    {
      A a;
      B b;
      C c;
      foo(a);  // Spezialisiert auf A
      foo(b);  // Spezialisiert auf A
      foo(c);  // Unspezialisiert
    }
    

    Das ist dann natürlich weniger universell als templates 😞



  • Da würde Liskov gar nicht gefallen. Du versuchst hier mit Compiletime-Polymorphie unterschiedlich auf Objekte(Aobj und Bobj) zu reagieren, die "im Prinzip" das gleiche sind.

    Benutze lieber virtuelle Funktionen und somit Laufzeit-Polymorphie. Dann können die Objekte selbst entscheiden, ob sie spezialisiert sein wollen oder nicht.



  • Schau dir mal boost::is_base_of<> an. Damit und mit dem Delegieren der Funktion an ein partiell spezialisiertes Functortemplate (Funktionstemplate kann man nicht partiell spezialisieren, nur überladen) wird sich was passendes bauen lassen.



  • pumuckl schrieb:

    Schau dir mal boost::is_base_of<> an. Damit und mit dem Delegieren der Funktion an ein partiell spezialisiertes Functortemplate (Funktionstemplate kann man nicht partiell spezialisieren, nur überladen) wird sich was passendes bauen lassen.

    Ehrlich gesagt verstehe ich noch nicht genug von der Materie, um das jetzt verstanden zu haben. Ich werde mich mal in das Thema einarbeiten.

    Danke an alle Tippgeber!



  • Hallo,
    das mit der Template Metaprogrammierung ist ziemlich komplex, deshalb die späte Antwort. Ich habe da mal was mit is_base_of gebastelt und das funktioniert auch wie gewünscht:

    class Base{};
    
    class A: public Base {};
    
    class B: public A{};
    
    class C: public Base{};
    
    template<class T, bool b> void foo(T t, const boost::integral_constant<bool, b>&);
    
    template<class T> void foo(T a, const boost::true_type&){
      cout<<"Auf A spezialisiert"<<endl;
      // Code verwendet immer static_cast<A>(a)
    }
    
    template<class T> void foo(T t, const boost::false_type&)
    {
      cout<<"Unspezialisiert"<<endl;
    }
    
    template<class T> void foo_caller(T t){
      foo(t, typename boost::is_base_of<A,T>());
    }
    
    int main()
    {
      A a;
      B b;
      C c;
      int d;
      foo_caller(a);  // Spezialisiert auf A
      foo_caller(b);  // Spezialisiert auf A
      foo_caller(c);  // Unspezialisiert
      foo_caller(d);  // Unspezialisiert
    }
    

    Wenn ich jetzt aber eine weitere Spezialisierung hinzufügen will, stehe ich vor dem Problem, dass is_base_of nur zwei Werte haben kann, true oder false. Um mehr als eine Spezialisierung unterscheiden zu können, habe ich daran gedacht, so eine Art Bitset zu verwenden, ungefähr so:

    template<class T> void foo_caller(T t){
      foo(t, typename boost::is_base_of<A,T>(), typename boost::is_base_of<Base,T>());
    }
    

    Das könnte und sollte man natürlich nochmals in einer Unterfunktion a la get_most_specialized_base<typename T>::type verstecken, aber das Prinzip bleibt das selbe.

    Jedoch kommt mir das ziemlich umständlich vor, ich bin aber mit dieser Templateprogrammierung wie gesagt noch nicht so weit fortgeschritten, deshalb weiß ich nicht, ob es da bessere Methoden gibt. Hat da jemand einen Tipp für mich?



  • du willst also im Grunde eine template-basierte Abfrage, Pseudocode:

    foo (T):
      if A baseof T
        fooA(T)
      else if B baseof T
        fooB(T)
      else if C baseof T
        fooC(T)
      else 
        fooStandard(T)
    

    Die Lösung wären Funktoren, die sind mit boost relativ leicht zu bauen. Folgender Ansatz:

    template <typename pred, typename A, typename B>
    struct Select;
    
    template <tyename A, typename B>
    struct Select<boost::true_t, A, B>
    {
      typedef A type; //fuer true
    };
    
    template <tyename A, typename B>
    struct Select<boost::false_t, A, B>
    {
      typedef B type; //fuer false
    };
    

    Dann ist das Ganze für einen Typ recht einfach zu machen:

    struct funA
    { 
      void operator()(A* pa); 
    };
    template<class T>
    struct funStandard
    { 
      void operator()(T* pt);
    };
    
    template<class T>
    void foo(T* pt)
    {
      typedef Select<boost::is_base_of<A, T>::type
                     funA,
                     funStandard<T> >::type funType;
      funType functor;
      functor(T);
    }
    

    für mehrere wirds dann nur wenig komplizierter:

    template<class T>
    void foo(T* pt)
    {
      typedef Select<boost::is_base_of<A, T>::type, funA,
              Select<boost::is_base_of<B, T>::type, funB,
              Select<boost::is_base_of<C, T>::type, funC,
                     funStandard<T> >::type >::type >::type funType;
      funType functor;
      functor(T);
    }
    


  • Ja, das ist es was ich suchte. Und viel kürzer und eleganter als mein 60 Zeilen Monstrum, das ich mir dafür gebastelt habe 🙂
    Dankeschön!



  • Man kann natürlich den ganzen boost::is_base_of krams noch wegkapseln:

    template <class Base, class BaseFun, class Else, class T = typename Else::T_type>
    struct ApplyIfInherits
    {
      typedef T T_type;
      typedef Select<boost::is_base_of<Base, T>::type, BaseFun, Else>::type FuncType;
    
      void operator() (T* pt)
      {
        FuncType func;
        func(pt);
      }
    };
    
    //struct funA usw. wie oben
    template<typename T>
    void foo(T* pt)
    {
      ApplyIfInherits<A, funA,
      ApplyIfInherits<B, funB,
      ApplyIfInherits<C, funC,
      ApplyIfInherits<D, funD, 
                         funStandard<T>, T> > > > functor;
      return functor(pt);
    }
    

Anmelden zum Antworten