Die Sache mit den templates



  • Hallo zusammen,
    ich habe soeben templates kennen gelernt. So wie ich es gelernt habe, handelt es sich dabei um eine Art Schablone. Wenn ich also eine Methode vereinbare, aber nicht alles darüber weiß, so kann ich ein template erstellen. Ich habe in einen vorigen Post schon mal ein kleines Programm gezeigt, dass einfach einen String vertauscht. Wenn ich nun beispielsweise nicht weiß, ob ich einen String oder eine Zahl vertauschen möchte, dann könnte ich ja folgendes schreiben:

    template <class AllTyp>
    void Exchange (AllTyp &x, AllTyp &y)
    {
      AllTyp z = x; x = y; y =z
    }
    

    Leider wird diese doch sehr interessante Sache fast gar nicht angesprochen. Meine Fragen sind nun:

    1.) Wann und wie bestimme ich denn dann, um welchen Typ (also String, int…) es sich handelt. Wie wende ich diese Methode an?

    2.) Hier handelt es sich ja um eine klassenlose Methode. Warum muss ich bei einem template trotzdem „class“ schreiben?

    3.) Kann ich auch ein template von einer Methode machen, die einer Klasse angehört?

    Ich hoffe, dass ihr mich versteht!
    Vielen Dank
    lg, freakC++



  • freakC++ schrieb:

    1.) Wann und wie bestimme ich denn dann, um welchen Typ (also String, int…) es sich handelt. Wie wende ich diese Methode an?

    Du gibst den Typen im Quelltext an, der Compiler erstellt während der Übersetzung eine Funktion mit den konkreten Typen.

    int a = 4;
    int b = 6;
    Exchange<int>(a, b);
    

    freakC++ schrieb:

    2.) Hier handelt es sich ja um eine klassenlose Methode. Warum muss ich bei einem template trotzdem „class“ schreiben?

    class dient hier als Schlüsselwort um anzuzeigen, dass AllTyp der zu definierende Datentyp ist. Du kannst auch typename verwenden, wenn es für Dich besser lesbar ist.

    freakC++ schrieb:

    3.) Kann ich auch ein template von einer Methode machen, die einer Klasse angehört?

    Ja, Du kannst sogar eine gesamte Klassenschablone definieren.



  • Anfangs gab es in Verbindung mit dem Schlüsselwort template tatsächlich nur das Schlüsselwort class. Über diesen Stolperstein sind wohl einige gefallen, denn class steht hier für jeden beliebigen Datentyp (auch die built-in Typen wie bool, int, double, etc.). Aus diesem Grund wurde später das Schlüsselwort typename ergänzt, das in der Definition von template Funktionen oder Klassen die gleiche Funktion besitzt wie class.
    Folgende Definitionen sind also identisch:

    template<class T>
    void template_func( const T& op );
    
    und
    
    template<typename T>
    void template_func( const T& op );
    

    Welche Funktion der Compiler aus einem template erzeugt hängt davon ab, ob er genügend Informationen über die Parameter und deren Typen erhält. Der Begriff dazu lautet template argument deduction. Angenommen, du hast folgende template Funktion:

    #include <iostream>
    
    template<typename T>
    void print( const T& op )
    {
       std::cout << op << endl;
    }
    
    int main()
    {
       int Integer = 17;
       double Double = 5.2;
    
       print( Integer );
       print( Double );
    }
    

    Beim Aufruf von print() gibst du der Funktion ein Argument eines bestimmten Typs mit, beim ersten Aufruf ist es ein int, beim zweiten ein double. Aufgrund dieser Argumente kann der Compiler selbstständig erkennen und entscheiden, welche Funktionen er erzeugen muss (einmal eine für den Typ int und eine für den Typ double).
    Anders sieht es aus, wenn diese Informationen fehlen (z.B. wenn keine Parameter übergeben werden. Dann fehlt dem Compiler jegliche Information über den benutzten Typ). In diesem Fall musst du explizit angeben, für welchen Typ du eine template Funktion erzeugen willst.

    template<typename T>
    T create_default()
    {
       T theT;
       return theT;
    }
    
    int main()
    {
       int DefaultInt = create_default<int>();
    }
    

    Genaugenommen musst du nur die template argument typen angeben, die der Compiler nicht automatisch erkennen kann. Nehmen mir mal eine einfache Funktion, die den Mittelwert aus zwei Zahlen bestimmen soll:

    template<typename T>
    double avg( T op1, T op2 )
    {
       return static_cast<double>( op1 + op2 ) / 2;
    }
    
    double average = avg( 3,4 );
    

    Funktioniert hervorragend für alle Zahlentypen. Naja, für fast alle. Probleme gibt´s dann, wenn du das für komplexe Zahlen machen willst, da reicht der double als Rückgabetyp nicht mehr aus. Also muss die Funktion um den Rückgabetyp erweitert werden:

    template<typename Result, typename Argument>
    Result avg( const Argument& op1, const Argument& op2 )
    {
       return static_cast<Result>( op1 + op2 ) / 2;
    }
    
    // angenommen, es gäbe den Datentyp complex_t (könnte ein 
    // typedef auf std::complex<double> sein.
    complex_t c1,c2,r;
    
    // explizite Spezifikation beider template argument typen
    r = avg<complex_t, complex_t>( c1,c2 );
    
    // explizite Spezifikation des ersten template argument typen,der zweite 
    // kann vom Compiler durch die übergebenen Variablen erkannt werden
    r = avg<complex_t>( c1,c2 );
    

    Den Typ für das zweite Template Argument kann der Compiler aus dem Aufruf generieren, da er ja die Parameter kennt. Den Typ für den Rückgabewert musst du allerdings explizit angeben, da der Compiler ihn nicht bestimmen kann.

    Wenn du dich ernsthaft mit templates auseinandersetzen willst kann ich dir folgende Bücher empfehlen:

    C++ Templates, The Complete Guide von David Candervoorde und Nicolai Josuttis
    Modern C++ Design von Andreji Alexandrescu (starker Tobak!)



  • DocShoe schrieb:

    Anfangs gab es in Verbindung mit dem Schlüsselwort template tatsächlich nur das Schlüsselwort class. Über diesen Stolperstein sind wohl einige gefallen, denn class steht hier für jeden beliebigen Datentyp (auch die built-in Typen wie bool, int, double, etc.). Aus diesem Grund wurde später das Schlüsselwort typename ergänzt, das in der Definition von template Funktionen oder Klassen die gleiche Funktion besitzt wie class.

    Der wesentliche Grund für die Einführung von typename waren AFAIK die Mehrdeutigkeitsprobleme, die sich bei Member-Typen von Templates ergaben - und die Möglichkeit der Verwendung von typename statt class für Template-Parameter war eher ein dadurch ermöglichter nützlicher Nebeneffekt. Das C++-Komitee ist sehr zurückhaltend beim Einführen neuer Schlüsselwörter und tut das gewöhnlich nur, wenn es unumgänglich ist 😕



  • Dieser Thread wurde von Moderator/in akari aus dem Forum VCL (C++ Builder) in das Forum C++ verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • audacia schrieb:

    DocShoe schrieb:

    Anfangs gab es in Verbindung mit dem Schlüsselwort template tatsächlich nur das Schlüsselwort class. Über diesen Stolperstein sind wohl einige gefallen, denn class steht hier für jeden beliebigen Datentyp (auch die built-in Typen wie bool, int, double, etc.). Aus diesem Grund wurde später das Schlüsselwort typename ergänzt, das in der Definition von template Funktionen oder Klassen die gleiche Funktion besitzt wie class.

    Der wesentliche Grund für die Einführung von typename waren AFAIK die Mehrdeutigkeitsprobleme, die sich bei Member-Typen von Templates ergaben - und die Möglichkeit der Verwendung von typename statt class für Template-Parameter war eher ein dadurch ermöglichter nützlicher Nebeneffekt. Das C++-Komittee ist sehr zurückhaltend beim Einführen neuer Schlüsselwörter und tut das gewöhnlich nur, wenn es unumgänglich ist 😕

    Das macht wohl mehr Sinn... habe meine Version aus ´ner Schulung aufgeschnappt und war mit der Erklärung auch zufrieden.



  • _DocShoe_ hat bezüglich der Anwendung von Templates schon einiges erläutert, hier gibts z.B. noch eine Einführung.

    Übrigens: Deine Exchange() -Funktion ist bereits in der C++-Standardbibliothek vorhanden; sie heisst std::swap() und befindet sich im Header <algorithm> (siehe auch hier).


Anmelden zum Antworten