[gelöst] Template "automatisch" spezialisieren



  • Hi,

    habe folgendes Problem:
    Um Sachen umzurechnen habe ich ein Klassentemplate, das etwa so aussieht:

    template <typename T, typename U = T>
    class Foo {
    public:
        struct Functor {
            U operator()(U) const { return 12 * value + 7; } //Beispiel
        }
    
        Foo (Functor* f):m_func(f) {}
    
        T bar(const T& value) const {
             return (*m_func)(value);
        }
    private:
        Functor* m_func;
    }
    

    Da ist spezialisiert für den Fall eines Vectors, so dass in dem Fall der Funktor elementweise angewendet wird:

    template<>
    std::vector<int> Foo<std::vector<int>, int>::bar(const std::vector<int>& value) const 
    {
        // *m_func auf jedes Element des Vectors anwenden und neuen Vector zurückgeben
    }
    

    Frage: Wie kann ich erreichen, dass das "automatisch" gemacht wird, wenn ein Vektor auftritt? Also so das folgendes passiert:

    Baz<int> => typedeft Foo<int> = Foo<int, int>
    Baz<std::vector<int> > => typedeft Foo<std::vector<int>, int>
    

    Zusammengefasst: Ich möchte den Typen "bestellen" können, den ich möchte, aber wenn das ein vector ist, soll das zweite template-argument der Elementtyp des Vektors sein.

    Hoffe, ich konnte es verständlich erklären. Schönen sonnigen Montag noch 🙂

    Phil



  • Okay, habe ein bißchen gebastelt. Hier ist mein erster Ansatz:

    template <typename T>
        struct FooFrontend {
            typedef Foo<T> Type;
            typedef T BasicT;
        };
    
        template <>
        struct FooFrontend<std::vector<int> > {
            typedef Foo<std::vector<int>, int> Type;
            typedef int BasicT;
        };
    

    und ggfs. weitere spezilaisierungen für gloat-vectoren usw.

    Ein Problem habe ich jetzt allerdings, wenn ich von Foo erbe, um andere Funktoren zu definieren. Beispiel:

    template <typename T>
    class CocreteFoo: public Foo<T, FooFrontend<T>::BasicT>
    {
        struct ConcreteFunctor: public Foo<T, FooFrontend<T>::BasicT>::Functor {
            FooFrontend<T>::BasicT operator()(FooFrontend<T>::BasicT value) const 
            { // mach irgendwas }
        };
    };
    

    Dann erzählt mir gcc:

    error: type/value mismatch at argument 2 in template parameter list for `template<class T, class U> class Foo'
    error: expected a type, got ` FooFrontend<T>::BasicT'
    

    Aber BasicT ist doch ein Typ!
    Irgendwo in meinem Hinterkopf geistert gerade "typedef typename" rum, muss mal den Meyers aus dem Schrank holen 😉

    In der Zeit bin ich weiter für konstruktive Vorschläge offen 🙂

    Phil


  • Mod

    Schreib mal ein typename davor, dein Compiler muss nämlich noch wissen, dass er FooFrontend<T>::BasicT als Typenbezeichnung interpretieren darf/soll. Denn es geht aus dem Kontext nicht hervor, dass es sich um einen Typen handelt. FooFrontend<T>::BasicT könnte ja auch beispielsweise ein int sein. Genaues weiß man erst, wenn man T kennt. Der Compiler will es aber vorher wissen, damit er hinterher weiß ob das T das er bekommt auch passt.



  • Bingo!!!

    class ConcreteFoo: public Foo<T, typename FooFrontend<T>::BasicT>
    {
        typedef typename FooFrontend<T>::BasicT BasicType;
    ...
    

    Klasse. Das geht!

    War meine Intuition ja gar nicht so verkeht.

    Aktenvermerk an mich selbst: Und schon wieder ein Stück Code produziert, was keiner mehr lesen kann, seufz.


  • Mod

    PhilippM schrieb:

    Aktenvermerk an mich selbst: Und schon wieder ein Stück Code produziert, was keiner mehr lesen kann, seufz.

    Ach, das ist relativ harmlos. Ein C++_Programmierer mit 1-2 Jahren Erfahrung kann das recht flüssig lesen. Und wenn das Funktionstemplate gut ist, sollte ja auch im Idealfall niemals wieder jemand den Code zu lesen brauchen.


Anmelden zum Antworten