Klassentemplate: Nur einzelne Methoden spezialisieren



  • Ich bins wieder... Jetzt habe ich zwar alles umgebaut, aber das programmieren an dieser Klasse ist nun eine einzige Qual, weil der Compiler JEDESMAL wenn ich innerhalb einer Methode von basic_matrix (spezialisiert) auf eine Methode von basic_matrix_base zugreifen will ich den vollen Namen der Funktion ausschreiben muss, also basic_matrix_base<T, 2, 2>::foo(). Das wird ziemlich schnell unübersichtlich. Ich bin so genervt, dass ich bald die ganze Klasse schrotten werden und eben zwei Klassen für 3x3- und 4x4-Matrizen schreiben werde. Selbst das wird weniger Redundanz als so.
    Gibt es im angeblich irgendwann kommenden neuen Standard wenigstens eine Möglichkeit, ein Klassentemplate gleichzeitig zu spezialisieren und zu erweitern? Oder hat jemand eine andere clevere Idee, die etwas schneller umzusetzen ist als der neue Standard? 😉
    genervter geloescht

    PS: matrix.hpp:108: Fehler: es gibt keine Argumente für »get_data«, die von einem Templateparameter abhängen, weshalb eine Deklaration von »get_data« verfügbar sein muss


  • Mod

    benutz doch einfach eine using-Deklaration, um den Funktionsnamen bekanntzumachen. Oder schreibt eine kurze inline Funktion, die die geerbte Funktion aufruft. Im neuen Standard könnte man das eleganter durch template typedefs lösen. Dein Problem ist ja, dass die erweiterte Version (für bestimmte Parameter) auf die Grundversion des Templates (mit den selben Parametern) aufbauen soll, und das schließt die unmittelbare Anwendung von Spezialisierung ja aus. Anderseits könnte evtl. die Verwendung eines zusätzlichen Templateparameters mit geeignetem defaultwert helfen. Etwa so:

    template<typename T, int m, int n, bool = (m==3&&n==3) /*hier alle Fälle, die erweitert werden sollen geeignet einschließen)*/>
    class basic_matrix; // wird nicht definiert
    
    // erste Spezialisierung, die Grundform:
    template<typename T, int m, int n>
    class basic_matrix<T, m, n, false>
    // ...
    
    // Spezialisierung für 3x3
    template<typename T>
    class basic_matrix<T, 3, 3, true>
        : public basic_matrix<T, 3, 3, false>
    // ...
    

    Ist systematisch ein bisschen unsauber, spart aber wohl ein wenig code

    Zu überlegen wäre auch noch, ob eine Spezialisierung des Templates überhaupt erforderlich ist. Du kannst ja erstens auch dem Primärtemplate zusätzliche Funktionen spendieren, deren Definitionen für nicht passende Templateargumente nicht instantiierbar sind (oder per SFINAE herausfallen) oder du spezialisiert einzelne Funktionen. Das ist aber auf explizite Spezialisierung beschränkt. Die Beschränkung könnte z.B. dadurch umgangen werden, das diese Funktionen selbst einen Funktor aufrufen, diesen wiederum könntest du auch wieder partiell (also für alle T) spezialisieren.



  • Hmmm, nun, das wird zwar etwas kürzer, aber so richtig zufrieden bin ich damit nicht. Fällt irgendwie unter den Begriff "frickeln" 😉 Ich benutze schon BOOST_STATIC_ASSERT() um Methoden, die nur mit m==n funktionieren zu definieren. Aber die selben Methoden für m==n==3 und m==n==2 müssen ja auch definiert werden und da liegt der Hund begraben. Eigentlich müsste man nur die spezialisieren, aber das geht nicht partiell. Ich kann wirklich nicht glauben, dass da sonst keiner dran gedacht hat. Oder ist meine Matrix-Klasse selbst schrottig? Wie auch immer, ich lasse das mal ruhen und komme wieder darauf zurück, wenn ich Muse habe. Ich finde es aber trotzdem schön, dass du dir Gedanken darüber machst, sonst wäre ich längst zu der Erkenntnis gelangt, dass ich einfach zu blöd dafür bin 😉


  • Mod

    geloescht schrieb:

    Aber die selben Methoden für m==n==3 und m==n==2 müssen ja auch definiert werden und da liegt der Hund begraben.

    Das geht doch ebenso einfach:

    template<typename T, int n>
    class basic_matrix<T, n, n, true>
    // ...
    

    so hast du zwar auch ein template wenn m,n nicht 2 oder 3 ist, aber dieses wird ja dank des bools im Regelfall nicht instantiiert.



  • öhm. Verstehe nicht. Wie kommen dann die zwei Methoden rein? Die sind ja für 2 und drei unterschiedlich.


  • Mod

    unterscheiden sich die Funktionen nur in ihrer Implementation?



  • Ja, und eben in der Klasse, der sie angehören.


  • Mod

    geloescht schrieb:

    Ja, und eben in der Klasse, der sie angehören.

    Dann ist das Problem weitaus einfacher zu lösen, als du anfangs dargestellt hast.
    per SFINAE:

    template<typename T, int m, int n>
    class basic_matrix
    {
    public:
    //    void foo(); wird zu
        typename enable_if_c<m!=m||m!=2&&m!=3,void>::type foo(); // der allgemeine Fall
        typename enable_if_c<m==n&&m==2,void>::type foo(); // m,n == 2
        typename enable_if_c<m==n&&m==3,void>::type foo(); // m,n == 3
    // usw.
    };
    

    Es gibt noch ein paar andere Möglichkeiten, das mittes enable_if zu machen , ich verweise dazu auf die Boost Doku

    Eine andere Möglichkeit:

    template<typename T, int m, int n>
    class basic_matrix
    {
    private:
        template<typename U=T,int o=m,intp=n> friend class DoFoo;
    public:
        void foo() { DoFoo<>()(this); }
    };
    
    template<typename T, int m, int n> class basic_matrix::
    template<typename U, int o, int p> class DoFoo
    {
        void operator()(basic_matrix<T,m,n>*) { /* ... */ }
    };
    
    template<typename T, int m, int n> class basic_matrix::
    template<> class DoFoo<T,2,2>
    // usw.
    

    Indirektion löst noch (fast) jedes Problem.



  • Wahrscheinlich wusste ich nicht, wie ich es anfangs so darstellen konnte, dass man es so lösen könnte 😉
    Das eröffnet neue Horizonte für mich. Auch wenn es wirklich seltsam aussieht. Dankeschön. Wenn ich das ganze wie in der Doku vorgeschlagen als Default-Parameter vom Typ void* mache dann wird der Parameter doch hoffentlich rausoptimiert, wenn ich ihn nicht benutze.



  • darf ich fragen warum du das nicht einfach mit ner if bedingung löst ?



  • Weil Templates zur Compilezeit erstellt werden und ifs zur Laufzeit ausgeführt werden. Außerdem wäre ein if unübersichtlich und etwas langsamer in der Ausführung, was ich nicht will, weil es um ein 3D-Renderingprogramm geht.



  • Ob das jetzt für dich sinnvoll ist oder nicht sei mal dahingstellt, dennoch es ist durchaus möglich relativ leicht if-Statments mit Hilfe von Templates nachzubilden.

    Hier mal ein Beispiel von: http://www.lrz-muenchen.de/~ebner/C++/Slides/cxxkurs.html

    // Klassendeklaration:
    template<bool C>
    class IfThen { };
    
    // Spezialisierung für beide Fälle
    class IfThen<true> {
      public:
        static inline void f()
        { statement1; }
    };
    
    class IfThen<false> {
      public:
        static inline void f()
        { statement2; }
    };
    
    // Ersatz des 'if/else'-Statements:
    IfThen<condition>::f();
    

    Gruß Baracke


Anmelden zum Antworten