Implizite Konvertierung für Template-Argumente



  • Guten morgen,

    ich will mal wieder was unmögliches ^^

    Nehmen wir an, ich habe einen Satz Typen, die sich implizit in Standardtypen konvertieren lassen:

    struct Wrapper32 {
        Wrapper32(int32_t i):data_(i) {}
        operator int32_t() { return data_; }
        int32_t data_;
    };
    
    struct Wrapper16 {
        Wrapper16(int16_t i):data_(i) {}
        operator int16_t() { return data_; }
        int16_t data_;
    };
    // usw, für chars, unsigned, whatever...
    

    Diese Wrapper haben in der echten Anwendung natürlich noch andere Member, die Metainformationen tragen. Ist hier aber nicht wichtig.

    Nehmen wir weiterhin an, es gibt ein Klassentemplate das so aussieht:

    template <typename T, typename U=T>
    class Foo {
        Foo();
        T memfun();
        void memfun(const U&);
    }
    

    Jetzt das Problem: U soll jetzt immmer der Wrappertyp von T sein. Also, wenn T==int32_t, dann U=Wrapper32. Rein Kompiliertechnisch geht das natürlich, da man U und T implizit ineinander konvertieren kann. Aber die Templategenerierung passiert ja BEVOR sich der Kompiler über implizite Konvertierung Gedanken macht.

    Daher: Gibt es eine kreative Lösung

    template <typename T, typename U = Wrapper(T)>
    

    auszudrücken?

    Gruß,
    Philipp



  • Nimm ne Metafunction:

    template <class T>
    struct MakeWrapperType;
    
    template <>
    struct MakeWrapperType<int32_t>
    {
        typedef Wrapper32 Type;
    };
    
    template <>
    struct MakeWrapperType<int16_t>
    {
        typedef Wrapper16 Type;
    };
    
    template <>
    struct MakeWrapperType<double>
    {
        typedef WrapperDouble Type;
    };
    
    //...
    
    template <typename T, typename U = MakeWrapperType<T>::Type>
    class Foo {
        Foo();
        T memfun();
        void memfun(const U&);
    };
    

    Habs nicht probiert, sollte aber so gehen.

    p.S.: noch einfacher wäre es natürlich wenn der Wrapper gleich selbst ein Template wäre:

    template <typename T, typename U = Wrapper<T> >
    class Foo {
        Foo();
        T memfun();
        void memfun(const U&);
    };
    

    Da das so offensichtlich ist bin ich mal davon ausgegangen dass du das aus irgend einem Grund nicht willst.



  • Ja, dass der Wrapper kein Template ist, hat leider Gründe. Sonst wäre es in der Tat einfach 🙂

    Aber dein Ansatz erscheint mir doch intuitiv benutzbar. So mach ichs, danke 🙂



  • hustbaer schrieb:

    Nimm ne Metafunction:

    template <typename T, typename U = MakeWrapperType<T>::Type>
    class Foo {
        Foo();
        T memfun();
        void memfun(const U&);
    };
    

    Habs nicht probiert, sollte aber so gehen.

    So geht's:

    template <typename T, typename U = typename MakeWrapperType<T>::Type>
    class Foo {
        Foo();
        T memfun();
        void memfun(const U&);
    };
    

    Man beachte das hinzugekommene typename. Woher sollte der arme Kompiler auch vorher wissen, dass MakeWrapperType<T>::Type ein Typ ist und nicht irgendeine Variable.

    Philipp



  • Hast Recht, da muss ein typename hin. (MSVC frisst es übrigens auch ohne, Comeau motzt aber)
    Könnte der Compiler allerdings leicht selbst checken, da es ja ein Default-Wert für nen Typ-Parameter ist. An der Stelle darf nie was anderes als ein Typ stehen.



  • Bei typedef könnte es der Compiler auch immer checken. Trotzdem muss ein typename hin. Auf eine Art ist diese Konsistenz auch gut: Von Template-Parametern abhängige Typen müssen immer speziell gekennzeichnet werden.


Anmelden zum Antworten