partielle template spezialisierung



  • Ich habe ein Klassentemplate:

    template <typename T, bool parameter = false> // in dem fall: ist die klasse thread-safe?
    class classname
    {
    	static void functionname ();
    };
    

    Und möchte diese Funktion jetzt (partiell) spezialisieren:

    template <typename T>
    void classname<T, true>::functionname()
    {
    	//;
    }
    
    template <typename T>
    void classname<T, false>::functionname()
    {
    	//;
    }
    

    Allerdings habe ich gerade gelesen, dass das nicht geht - geht das gar nicht oder gibts da doch irgend nen tollen workaround/...? ^^
    Und vor allem: (also die eigentliche frage)
    Wieso geht das nicht (offenbar ist es ja im standard gar nicht vorgesehen, dass das geht - aber wieso will man das nicht, ich hab auch gerade keine änderung darüber im aktuellen working draft gesehen - kann aber auch nicht behaupten, ewig danach gesucht zu haben)?

    Das es auch ganz einfach über nen if in der Funktion geht, ist mir schon klar (zumindest in dem fall, also bei nem einfachen boolean) - allerdings kann es ja nicht immer nur so gelöst werden!?

    bb


  • Administrator

    unskilled schrieb:

    Allerdings habe ich gerade gelesen, dass das nicht geht - geht das gar nicht oder gibts da doch irgend nen tollen workaround/...? ^^

    Partielle Funktionsspezialisierungen gehen nicht. Es gibt allerdings einen Workaround, welcher ein wenig komplex ist. Dazu später.

    unskilled schrieb:

    Und vor allem: (also die eigentliche frage)
    Wieso geht das nicht (offenbar ist es ja im standard gar nicht vorgesehen, dass das geht - aber wieso will man das nicht, ich hab auch gerade keine änderung darüber im aktuellen working draft gesehen - kann aber auch nicht behaupten, ewig danach gesucht zu haben)?

    Keine Ahnung! Es will halt nicht, wieso leuchtet mir nicht ein. Womöglich ist es technisch zu schwer umzusetzen, da wahrscheinlich eine partielle Spezialisierung unterschiedlich erreicht wird, ob es nun für eine Klasse oder für eine Funktion ist. Aber da bin ich wild am raten 😉

    unskilled schrieb:

    Das es auch ganz einfach über nen if in der Funktion geht, ist mir schon klar (zumindest in dem fall, also bei nem einfachen boolean) - allerdings kann es ja nicht immer nur so gelöst werden!?

    Die if -Lösung wäre aber gar nicht so schlecht! Im Release-Modus, bzw. einfach bei den Optimierungen, erkennt der Kompiler Bereiche, welche nie erreicht werden können und schneidet sie raus. Die if -Anweisung könnte daher völlig wegoptimiert werden.

    Der Workaround geht übrigens so:

    // Ich zeige es nur an normalen Funktionen, der Einfachheit halber ;)
    
    // Zuerst zwei Hilfsklassen:
    template<int>
    struct Int2Type { };
    
    template<bool, typename T1, typename T2>
    struct Select
    {
      typedef T1 Result;
    };
    
    template<typename T1, typename T2>
    struct Select<false, T1, T2>
    {
      typedef T2 Result;
    };
    
    // Zwei Hilfsfunktionen, welche die Spezialisierungen darstellen:
    template<typename T>
    void foo_helper(Int2Type<true>)
    {
    }
    
    template<typename T>
    void foo_helper(Int2Type<false>)
    {
    }
    
    template<typename T, bool B>
    void foo()
    {
      // Spezialisierung auswählen:
      foo_helper<T>(
        Select
        <
          B,
          Int2Type<true>,
          Int2Type<false>
        >
        ::Result());
    }
    

    Die LOKI Bibliothek kann einem da behilflich sein, da sie Int2Type und Select bereits schon kennt 😉

    Grüssli



  • Naja das ist ja keine Spezielisierung eines Funktions-Templates, sondern eine Spezialisierung einer Memberfunktion eines Klassentemplates.
    Dafür gelten wieder andere Regeln soweit ich weiss.

    Lies im Standard nach 😉



  • Jopp - das mit dem if ist mir schon klar (also, dass es rausoptimiert wird etc)... es ging mir dabei nicht darum, dass es schneller sein sollte oder so...

    das es über ne helper-class geht, war mir fast scho klar, aber das lohnt die Mühe nicht ^^

    Dravere schrieb:

    Keine Ahnung! Es will halt nicht, wieso leuchtet mir nicht ein. Womöglich ist es technisch zu schwer umzusetzen, da wahrscheinlich eine partielle Spezialisierung unterschiedlich erreicht wird, ob es nun für eine Klasse oder für eine Funktion ist. Aber da bin ich wild am raten

    Gerade das hätte mich aber eben interessiert... 😃
    Und der Unterschied leuchtet mir eben iwie nicht so richtig ein - ich meine, klar gibts da nen Unterschied - aber kann doch nich sein, dass es das Standard-Komitee interessiert, wie kompliziert etwas umzusetzen ist (siehe export ^^) - und so viel komplizierter wird es ja wohl nicht sein - auch, wenn ich mich nicht so wirklich mit der (Theorie der) Programmierung eines Compilers beschäftigt habe, würde ich das einfach mal so behaupten wollen 😉

    hustbaer schrieb:

    Lies im Standard nach

    lies halt wenigsten meinen post mal durch... da steht schon, dass ich weiß, dass es im standard nirgendwo 'erlaubt' wird und auch, dass ich im aktuellen working draft nix anderes gefunden habe... und ob es geht oder nicht, stand ja auch gar nicht mehr zur frage...

    bb



  • Partielle Spezialisierung ist das doch eh nicht, da hier nicht nach einem Typ spezialisiert wird, sondern nach Werten, die der Typ annimmt. Oder habe ich da die partielle Spezialisierung falsch verstanden?



  • hustbaer schrieb:

    Naja das ist ja keine Spezielisierung eines Funktions-Templates, sondern eine Spezialisierung einer Memberfunktion eines Klassentemplates.
    Dafür gelten wieder andere Regeln soweit ich weiss.

    Nö. Aus template-Sicht sind Methoden von Klassentemplates ganz normale Funktionstemplates, mit der Ausnahme dass man keine Teile von Klassen spezialisieren kann (ergo auch keine einzelnen Methoden), sondern nur die ganze Klasse. Davon abgesehen kann man Funktionstemplates (umd Methodentemplates) nicht partiell spezialisieren, kann aber etwas ähnliches durch Überladung mti einem weiteren Funktionstemplate erreichen:

    template <class A, class B> 
    void f(A a, B b) {} //(1)
    
    template <class A>
    void f<A,A>(A a, A b) {} //(2) Illegal, partielle Spezialisierung nicht erlaubt
    
    template <class A>
    void f(A a, A b) //(3) so aehnlich, ist aber keine Spezialisierung sondern eine Überladung.
    
    int main()
    {
      f(2, 5); //ruft (3) auf, f<int>(int, int)
      f(2.5, 5); //ruft (1) auf, f<double, int>(double, int)
      f<double>(2.5, 5); //ruft (3) auf, mit Konvertierung 5 -> 5.0
      f<int, int>(2, 5); //ruft (1) auf, weil explizit die erste Version aufgerufen wurde.
    }
    

    Warum der Standard so entschieden hat steht bestimmt in irgendwelchen rationale zum entsprechenden draft.

    Was den Workaround angeht: Ein allgemeiner Workaround für partielle Funktionsspezialisierung ist folgender:
    - delegiere den Funktionsaufruf an eine statische Methode eines Klassentemplates
    - spezialisiere das Klassentemplate partiell.

    template<class T, bool parameter = false>
    class classname 
    {
      template <bool B>
      struct fun_part_spec_helper 
      { 
        static void invoke() {/* specialization for B=true */} 
      };
      template <>
      struct fun_part_spec_helper<false> 
      { 
        static void invoke() {/* specialization for B=false */}
      };
    
    public:
      static void functionname()
      { fun_part_spec_helper<parameter>::invoke(); }
    };
    

    Macht genau das was du möchtest und hat außer etwas mehr Tipparbeit keine Nachteile:
    - es werden keine zusätzlichen Informationen ausgegeben.
    - es wird nur die Spezialisierung von fun_part_spec_helper instantiiert, die für die jeweilige Templateklasse angemesse ist. z.B. wird in classname<int, true> nur fun_part_spec_helper<true> erwähnt, also wird fun_part_spec_helper<false> nicht instantiiert.
    - da es sich bei functionname() nur um den Aufruf einer einzelnen Funktion handelt wird wahrscheinlich jeder Compiler der dessen fähig ist den Aufruf inlinen und du hast ein Verhalten genau so als könntest du die einzelne Methode partiell spezialisieren.



  • jencas schrieb:

    Partielle Spezialisierung ist das doch eh nicht, da hier nicht nach einem Typ spezialisiert wird, sondern nach Werten, die der Typ annimmt. Oder habe ich da die partielle Spezialisierung falsch verstanden?

    Ja hast du. Egal ob man bei templates einem Wert-Parameter einen Wert oder einem Typ-Parameter einen Typen zuweist (oder einem Template template-parameter ein template, wenn wir schonmal dabei sind), es handelt sich um eine Spezialisierung, und die ist immer dann partiell, wenn nicht allen Templateparametern etwas zugeordnet worden ist.



  • hole

    der ?: operator wird schon zur compilezeit ausgewertet. warum nicht

    template<class T, bool param = false>
    class test
    {
    	public:
    		static void func(void) { param ? func_true() : func_false(); }
    
    		static void func_true(void) { };
    		static void func_false(void) { };
    };
    

    ?

    meep meep



  • Meep Meep schrieb:

    template<class T, bool param = false>
    class test
    {
    	public:
    		static void func(void) { param ? func_true() : func_false(); }
    
    		static void func_true(void) { };
    		static void func_false(void) { };
    };
    

    scheint mir die hübscheste lösung zu sein 🙂
    erklärung hab ich zwar immernoch keine, wieso das nicht geht etc - aber man kann ja auch nich alles haben ;o)

    ty + bb



  • Meep Meep schrieb:

    der ?: operator wird schon zur compilezeit ausgewertet.

    Wenn er ausgewertet werden kann. Ich bin nicht sicher ob da auch zugehört, dass das 2. und 3. Argument ausgewertet werden können müssen.
    Selbst wenn würde das bedeuten dass für jede Instantiierung des Klassentemplates eine unnötige Methode mitgeneriert wird die nie aufgerufen wird.



  • pumuckl schrieb:

    Meep Meep schrieb:

    der ?: operator wird schon zur compilezeit ausgewertet.

    Wenn er ausgewertet werden kann. Ich bin nicht sicher ob da auch zugehört, dass das 2. und 3. Argument ausgewertet werden können müssen.
    Selbst wenn würde das bedeuten dass für jede Instantiierung des Klassentemplates eine unnötige Methode mitgeneriert wird die nie aufgerufen wird.

    na dann halt vielleicht so:

    template<class T, bool param = false>
    class test
    {
    	public:
    		static std::string func(void) { return my_func<T>(reinterpret_cast<test<T, param>*>(0)); }
    
    	private:
    		template<class S>
    		static std::string my_func(test<T, true>*) { return std::string("true"); }
    
    		template<class S>
    		static std::string my_func(test<T, false>*) { return std::string("false"); }
    };
    

    wobei, wenn der ?: zur comilezeit ausgewertet werden kann, wird nur die aufgerufene funktion generiert.

    meep meep



  • Meep Meep schrieb:

    wenn der ?: zur comilezeit ausgewertet werden kann, wird nur die aufgerufene funktion generiert.

    Nein, es wird dann nur der eine Funktionsaufruf generiert, die Funktion selber wird aber als fester Bestandteil der Klasse mitgeneriert, genuso wie alle anderen Methoden auch. Dabei ist egal ob sie jemals aufgerufen wird (bzw. dass sie nie aufgerufen wird).

    Meep Meep schrieb:

    na dann halt vielleicht so:

    template<class T, bool param = false>
    class test
    {
    	public:
    		static std::string func(void) { return my_func(reinterpret_cast<test<T, param>*>(0)); }
    
    	private:
    		static std::string my_func(test<T, true>*) { return std::string("true"); }
    
    		static std::string my_func(test<T, false>*) { return std::string("false"); }
    };
    

    Ist auch nicht besser, im zweifel sogar schlimmer: Es werden wieder beide Funktionen generiert, da sie beide gleich Bestandteil der Klasse sind. Ich bin mir bei der Deklaration von Pointern auf templates nicht ganz sicher wie die Instantiierungsregeln sind, aber es könnte sogar sein dass durch die pure Erwähnung des Pointers der jeweilige Typ generiert wird. Dann würde das bedeuten dass du immer wenn test<T, true> generiert wird durch die überladene methode auch gleich die Generierung von test<T,false> mit am Hals hast. Ich vermute zwar, dass das bei Pointern nicht der Fall ist, aber dennoch hast du immernoch beide Funktionen generiert und damit nichts gewonnen.



  • pumuckl schrieb:

    Nein, es wird dann nur der eine Funktionsaufruf generiert, die Funktion selber wird aber als fester Bestandteil der Klasse mitgeneriert

    ich war bis jetzt der meinung das nur memberfunktionen die benoetigt werden, auch erstellt werden.
    muss mich da nochmal schlau machen

    meep meep



  • pumuckl schrieb:

    Ich bin mir bei der Deklaration von Pointern auf templates nicht ganz sicher wie die Instantiierungsregeln sind, aber es könnte sogar sein dass durch die pure Erwähnung des Pointers der jeweilige Typ generiert wird.

    das glaub ich kaum. es funktioniert auch so:

    template<class T, bool param>
    class dummy;
    
    template<class T, bool param = false>
    class test
    {
        public:
            static std::string func(void) { return my_func(reinterpret_cast<dummy<T, param>*>(0)); }
    
        private:
            static std::string my_func(dummy<T, true>*) { return std::string("true"); }
    
            static std::string my_func(dummy<T, false>*) { return std::string("false"); }
    };
    


  • pumuckl schrieb:

    Meep Meep schrieb:

    der ?: operator wird schon zur compilezeit ausgewertet.

    Wenn er ausgewertet werden kann. Ich bin nicht sicher ob da auch zugehört, dass das 2. und 3. Argument ausgewertet werden können müssen.
    Selbst wenn würde das bedeuten dass für jede Instantiierung des Klassentemplates eine unnötige Methode mitgeneriert wird die nie aufgerufen wird.

    Es geht mir nicht darum, ob eine (winzigkleine) Funktion generiert und mitgelinkt wird - aber es trennt die beiden Funktionen eben sauber - und da ich meinem Compiler so und so immer blind vertraue, wird er es schon genau so machen, wie ich das möchte - er würde es nicht übers Herz bringen, mich zu enttäuschen 😉 außerdem ist die funktion jetzt nicht so extrem laufzeitkritisch und so viele instanzen dieser klasse wird es auch nicht geben...

    zu "Meep Meep"`s letztem Vorschlag: Der ist mir zu hässlich 😛

    bb


Log in to reply