Was kosten Templates?



  • Hi Leute! Was ich mich grad frage ist, ob Templates eigentlich was kosten? Ich mein, arbeiten diese intern mit Zeigern oder ähnlichem, sodass diese langsamer laufen als eine nicht-Template Klasse?

    Falls nicht, würde ich drüber nachdenken einige Sachen mit Templates nach zu rüsten.


  • Administrator

    Templates werden zur Kompilezeit ausgewertet. Zur Laufzeit hast du dann ganz normale Klassen und Funktionen.

    Grüssli



  • Um es der Verständlichkeit wegen noch an einem Beispiel zu erläutern:
    Aus

    template<class T>
    void ausgabe(T var)
    {
    	std::cout << var;
    }
    
    int main()
    {
    	ausgabe(0);
    	ausgabe("Hallo");
    }
    

    macht der Compiler vom Prinzip her folgendes:

    void ausgabe_int(int var)
    {
    	std::cout << var;
    }
    
    void ausgabe_constcharptr(const char *var)
    {
    	std::cout << var;
    }
    
    int main()
    {
    	ausgabe_int(0);
    	ausgabe_constcharptr("Hallo");
    }
    

    Template heißt übersetzt Schablone und genau das ist es auch. Eine Schablone für Funktionen oder Klassen, aus der der Compiler selbstständig konkrete Funktionen und Klassen generieren kann. Zur Laufzeit gibt es deshalb die Templates gar nicht mehr, sondern nur noch die generierten Funktionen und Klassen (wobei die ja zur Laufzeit auch nicht mehr existieren).



  • @Code-Walker:
    Templates kannst du was das angeht als eine Art "Super-Makros" verstehen.

    Es werden im Endeffekt ganz normale Klassen oder Funktionen erstellt, die ganz genau so funktionieren, als ob man sie mit Hand geschrieben hätte.

    Was allerdings zu schlechterer Performance führen kann, ist, wenn man zu viel Code über Templates erzeugt. Dadurch wird das Programm grösser, und die Chancen dass die viel genutzten Bereiche nicht mehr in den CPU Cache passen steigen. Hat allerdings nur indirekt mit Templates zu tun. Den Effekt hätte man genau so, wenn man die gleiche Menge Code mit Hand schreiben müsste.

    Stichwort: Template Bloat

    Und natürlich muss man, wenn man alles schön generisch halten möchte, manchmal einige Dinge komplizierter schreiben, als wenn man nur mit einem exakt definierten Typ zu tun hat. Viel davon kann der Compiler so optimieren, dass der komplizierter aussehende generische Code im Endeffekt gleich schnell läuft wie die einfachere nicht-generische Variante. Einiges aber auch nicht.



  • man kann allerdings auch viel optimieren weil man gewisse sachen dazu bringen kann schon zur compilezeit ausgerechent zu werden

    dazu steht was bei wikipedia unter dem punkt metaprogrammierung oder ähnlichem



  • Templates werden immer direkt zur Compiletime ausgewertet. Daher rührt auch nicht der Overhead. Wurde aber auch schon geschrieben.


  • Mod

    Skym0sh0 schrieb:

    man kann allerdings auch viel optimieren weil man gewisse sachen dazu bringen kann schon zur compilezeit ausgerechent zu werden

    dazu steht was bei wikipedia unter dem punkt metaprogrammierung oder ähnlichem

    Ich würde da so gerne mal ein praxistaugliches Beispiel zu sehen. Alles was ich dazu kenne sind akademische Szenarien oder gelangweilte Programmierer die das zum Spaß machen.



  • mh es gab da mal was zum error zur compile zeit ausgeben oder so

    ich schreib morgen mal das beispiel rein
    hab nur jetzt keine zeit mehr...


  • Mod

    Skym0sh0 schrieb:

    mh es gab da mal was zum error zur compile zeit ausgeben oder so

    ich schreib morgen mal das beispiel rein
    hab nur jetzt keine zeit mehr...

    Ja, oswas kenne ich. Ich meine aber wo man tatsächlich Sachen zur Compilezeit vorausberechnet und das tatsächlich praktisch relevant ist. Zum Beispiel schreibt niemand eine TMP-Fakultät um dann fakultaet<6> anstatt 720 schreiben zu können. Und selbst wenn man eine Fakultätsfunktion schreibt und fakultaet(6) schreibt, würde das jeder Compiler auch ohne TMP zu 720 optimieren.

    Ich frage mich halt, ob es Beispiele gibt, wo ein Programm tatsächlich schneller würde durch TMP.



  • Ich denke, Berechnungen über Template-Metaprogrammierung werden – falls sie überhaupt zum Einsatz kommen – eher ausgeführt, um einen Wert schon zur Compilezeit zu haben. Gewisse C++-Sprachmittel benötigen konstante Ausdrücke. Indirekt kann vielleicht dadurch eine Optimierung zustandekommen (z.B. indem man statische Arrays statt dynamischen vewendet, oder abhängig von einem Resultat einen anderen Ablaufpfad wählt).

    Den anderen Fall, wo Compilezeit-Berechnungen direkt benötigt werden, habe ich bisher kaum angetroffen. Man ist auch etwas eingeschränkt, weil z.B. keine Fliesskommazahlen verwendet werden können.

    Viel häufiger sieht man die Template-Metaprogrammierung aber im Zusammenhang mit statischem Dispatchen von Typen (Traits, Static Assertions, Policies, ...).



  • -.-



  • Templates sind gerade für 1,50 Euro im Angebot...

    und 4 für 5 Euro...



  • hier ist mein beispiel
    ist aus der 6. auflage von "C++ - einführung und professionelle programmierung" von ulrich breymann (S. 260)

    #include <iostream>
    
    template<int p, int i>
    struct istPrimzahl {
        enum {prim=(p%i) && istPrimzahl<(i>2?p:0), i-1>::prim};
    };
    
    template<int i>
    struct druckePrimzahlenBis {
        druckePrimzahlenBis<i-1> a;
        enum { prime = istPrimzahl<i, i-1>::prim };
    
        druckePrimzahlenBis() {
            if ( prime )
                std::cout << i << std::endl;
        }
    };
    
    struct istPrimzahl<0, 0> { enum {prim=1};};
    struct istPrimzahl<0, 1> { enum {prim=1};};
    struct druckePrimzahlenBis<2> { enum{prim=1}; };
    
    int main() {
        druckePrimzahlenBis< __17__ > a;
        return 0;
    }
    

    Dieses Programm wurde 1994 nach einer Idee von Erwin Unruh geschrieben, der ein programmm konstruierte, das beid er Übersetzung Primzahlen in den Fehlermeldungen des Compilers erzeugte.



  • Abgesehen, das der Code fehler hat, kommt man auch nicht weit als über IsPrim(500) zu berechnen 😞

    #include <iostream> 
    
    template<int p, int i> 
    struct istPrimzahl { 
        enum {prim=(p%i) && istPrimzahl<(i>2?p:0), i-1>::prim}; 
    }; 
    
    template<int i> 
    struct druckePrimzahlenBis { 
        druckePrimzahlenBis<i-1> a; 
        enum { prime = istPrimzahl<i, i-1>::prim }; 
    
        druckePrimzahlenBis() { 
            if ( prime ) 
                std::cout << i << std::endl; 
        } 
    }; 
    
    template<> 
    struct istPrimzahl<0, 0> { enum {prim=1};};
    
    template<> 
    struct istPrimzahl<0, 1> { enum {prim=1};}; 
    
    template<> 
    struct druckePrimzahlenBis<2> { enum{prim=1}; }; 
    
    int main() { 
        druckePrimzahlenBis< 501 > a; 
        return 0; 
    }
    

    Error 1 error C1202: recursive type or function dependency context too complex e:\workspace\cxx01\cxx01\main.cpp 5 1 cxx01


  • Mod

    Danke. Sowas meinte ich. Eine nichttriviale Aufgabe die während der Compilezeit gelöst werden kann. 👍



  • ich weis dass der code nicht klappt
    hat er bei mir auch nicht

    aber ich hab ihn wortwörtlich abgeschrieben aus dem buch vorhin, genauso wie die 3 zeilen danach

    aber das prinzip sollte klar sein dabei denke ich
    und die cracks hier können den auch so ohne probleme von syntaktischen rerrors befreien



  • SeppJ schrieb:

    Danke. Sowas meinte ich. Eine nichttriviale Aufgabe die während der Compilezeit gelöst werden kann. 👍

    TMP ist turingvollständig, also kann man alle Algorithmen damit zur Compilezeit lösen. Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.



  • ipsec schrieb:

    Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.



  • Shade Of Mine schrieb:

    ipsec schrieb:

    Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.

    Ich denke, dann kommt der Horror 😛

    #define TYPELIST_1(T1) typelist<T1, null_typelist>
    #define TYPELIST_2(T1, T2) typelist<T1, TYPELIST_1(T2) >
    #define TYPELIST_3(T1, T2, T3) typelist<T1, TYPELIST_2(T2, T3) >
    #define TYPELIST_4(T1, T2, T3, T4) typelist<T1, TYPELIST_3(T2, T3, T4) >
    


  • Shade Of Mine schrieb:

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.

    ahja, das lässt sich nicht hartcodieren, oder was


Log in to reply