Verwendung und erstellung von templates.



  • Hallo !
    Wie der Titel schon sagt , würde ich gerne wissen , wie ,wo und warum man templates benutzt.
    Ich habe mir die templates schon angesehen , doch konte ich nicht wircklich einen nutzen daraus für mich erkennen.

    Es wäre schön , wenn mir jemand das mal näher erläutern würde , vielleicht mit einem kleinen beispiel.

    Dankeschön



  • Wenn du eine Funktion oder eine Klasse als allgemeines Konzept ausdrücken möchtest, das nicht direkt an den Datentyp gebunden ist, aber trotzdem typsicher bleibt. (Das meiste ginge halt auch mit void* Pointern, ist aber wesentlich frickeliger, soll heißen, schlechter zu schreiben, schlechter zu lesen und damit auch schlechter zu warten.)

    Schönes Beispiel ist die Funktion qsort() aus C (void* Pointer) und die Funktion std::sort aus C++.



  • Hi,

    ich möchte dir das an einem Beispiel kurz erklären.

    //für die Multplikation von 2 Zahlen brauchst du
    
    int multiInt(int,int);
    double multiDouble(double,double);
    short multiShort(short, short);
    
    //mehrere solcher Methoden aber als template nur eine:
    template <typename T>
    T mutli(T x, T y)
    {
        return x*y;
    }
    

    du kannst sie ganz gewöhnlich mit multi(5,6) oder multi(5.0,6.0);
    Im ersten Fall wird int und im Zwieten Fall T mit double ersetzt 🙂
    Ich hoffe konnte dir ein wenig helfen.



  • Sieh dir mal die STL (Standard Template Library) und die STL-Algorithmen an. Nur durch Templates wurden Container überhaupt "möglich", und wenn du dir mal Sachen wie printf und cout vergleichst, kannst du ja gleich sehen, wo der Sinn liegt.

    std::string HW("Hello World!");
    
    printf("%s", HW.c_str());//Musst den Typ angeben
    
    std::cout << HW;//erklärt sich von selbst
    


  • Hacker schrieb:

    std::cout << HW;//erklärt sich von selbst
    

    Das hat ungefähr gar nichts damit zu tun, dass es sich bei diesem operator<< um eine Funktionsvolage handelt, sondern ginge auch mit normaler Funktionsüberladung. Allerdings müsste man ohne Templates für std::string und std::wstring zweimal praktisch den gleichen Code schreiben (und auch den praktisch gleichen operator<<).

    Interessanter wird es aber da, wo es um Typen geht, die noch nicht bekannt sind, wenn die Template geschrieben wird. Zum Beispiel ist es mir problemlos möglich,

    struct point {
      double x;
      double y;
    };
    
    bool operator<(point const &lhs, point const &rhs) {
      return lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y);
    }
    
    ...
    
    std::vector<point> v;
    std::sort(v.begin(), v.end());
    

    schreiben, obwohl weder der Autor von std::vector noch der von std::sort etwas von meinem Typen point wussten.

    Noch viel interessanter wird es da, wo man anhand der Eigenschaften bestimmter Typen passenden Code auswählt (mithin Template-Metaprogrammierung betreibt). Ich nehme als einfaches Beispiel mal die Implementation von std::distance aus GNUs libstdc++:

    template<typename _InputIterator>
        inline typename iterator_traits<_InputIterator>::difference_type
        __distance(_InputIterator __first, _InputIterator __last,
                   input_iterator_tag)
        {
          // concept requirements
          __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
    
          typename iterator_traits<_InputIterator>::difference_type __n = 0;
          while (__first != __last)
            {
              ++__first;
              ++__n;
            }
          return __n;
        }
    
      template<typename _RandomAccessIterator>
        inline typename iterator_traits<_RandomAccessIterator>::difference_type
        __distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
                   random_access_iterator_tag)
        {
          // concept requirements
          __glibcxx_function_requires(_RandomAccessIteratorConcept<
                                      _RandomAccessIterator>)
          return __last - __first;
        }
    
      template<typename _InputIterator>
        inline typename iterator_traits<_InputIterator>::difference_type
        distance(_InputIterator __first, _InputIterator __last)
        {
          // concept requirements -- taken care of in __distance
          return std::__distance(__first, __last,
                                 std::__iterator_category(__first));
        }
    

    Wodurch Random-Access-Iteratoren wie Zeiger oder std::vector<>::*iterator wesentlich effizienter behandelt werden können als etwa std::istream_iterator oder std::list<>::*iterator, bei denen der Abstand nicht direkt ausgelesen werden kann, sondern mühsam quasi ausprobiert werden muss.

    Man kann auf diese und ähnliche Weisen natürlich noch wesentlich komplexere Dinge machen. Auf die Spitze getrieben haben es wohl die Leute von Boost.MPL (wahlweise die von Boost.Proto), aber was das macht, krieg ich nicht in einem Forumspost erklärt.



  • seldon schrieb:

    Hacker schrieb:

    std::cout << HW;//erklärt sich von selbst
    

    Das hat ungefähr gar nichts damit zu tun, dass es sich bei diesem operator<< um eine Funktionsvolage handelt, sondern ginge auch mit normaler Funktionsüberladung. Allerdings müsste man ohne Templates für std::string und std::wstring zweimal praktisch den gleichen Code schreiben (und auch den praktisch gleichen operator<<).

    Das war garnicht der Punkt. Willst du den Operator für jeden einzelnen Basisdatentypen überladen?



  • Ok danke 😃
    Ich hab es jetzt sowit verstanden , das ich jeztz wircklich unabhängie funktionen schreiben kann.

    Ich hab das mit dem

    template<typname T>
    

    so verstanden ,das T jeder type sein kann ?!

    Nun hab ich aber noch so was gesehen :

    template<class T>
    

    Was ist das denn ?



  • _D schrieb:

    Ok danke 😃
    Ich hab es jetzt sowit verstanden , das ich jeztz wircklich unabhängie funktionen schreiben kann.

    Ich hab das mit dem

    template<typname T>
    

    so verstanden ,das T jeder type sein kann ?!

    Nun hab ich aber noch so was gesehen :

    template<class T>
    

    Was ist das denn ?

    Erstens heißt es typename, zweitens ist da (in diesem Zusammenhang!) kein Unterschied.
    Lies das nochmal hier durch.

    Edit:
    Ahh, hab was vergessen. Bei template template Parametern muss es class heißen 😉

    Und noch was: Ja, T kann jeder Typ sein, mit ihm können dann aber auch nur(und auch nur überladene, wenns ne Klasse ist) Operatoren genutzt werden.



  • Also wie jetzt das ist kein unterschied aber Bei template template Parametern muss es class heißen.

    Tut mir leid , aber eine kleine erklärung oder Beispiel wären ganz schön



  • template<class eins,template<class> typename zwei>// Muss nicht typename sondern class
    class Foo
    {
    };
    

    Zwei definiert eine Klasse, also muss da class hin.

    template<class T>/// Scheißegal
    T bigger(T& a, T& b){
        return ((a>b)?a:b);
    }
    


  • _D schrieb:

    Also wie jetzt das ist kein unterschied aber Bei template template Parametern muss es class heißen.

    typename und class sind in diesem Zusammenhang gleichwertig. Warum es beides geht steht in dem Link von Hacker doch gut beschrieben.

    @Hacker
    Natürlich sind die operator << für Basistypen einzeln überladen. Oder willst du bei "char" lieber die dezimale Darstellung sehen? Was machst du mit void*? Oder mit Fließkommazahlen? Da sind überall unterschiedliche Verhaltensweisen gefordert.



  • Ahh jaaa 😃
    Dann

    cout << HW << endl;//endl ist ein Template
    

    Edit: Ja, cooky, bin schon ziemlich behindert. Erst vor 'ner Stunde sah ich noch das.



  • Muss dich enttäuschen, endl ist auch kein template sondern eine normale Funktion.



  • Blubbfisch schreibt Mist
    Edit: n3242: 27.7.1

    template <class charT, class traits>
    basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
    

    Wobei die Templateargumente natürlich nur weitergegeben werden, ist jetzt nicht gerade das Beispiel für Templates.



  • 314159265358979 schrieb:

    Muss dich enttäuschen, endl ist auch kein template sondern eine normale Funktion.

    Nein. Oder ist das gar spezifisch?
    Denn
    Aber ich hab mal in nem PDF von ner UNI gelesen:

    endl ist ein Template, auch wenn es von vielen nicht als solches gesehen wird.

    Eig. wär es trotzdem derbst unnötig, draus 'n template zu machen.

    Wolln wir Spielen bei der Wiese?



  • Nö, woll ich nicht.



  • Hacker schrieb:

    Eig. wär es trotzdem derbst unnötig, draus 'n template zu machen.

    Nö. Aus endl ein Template zu machen ist genauso nötig/unnötig wie aus basic_ostream eines zu machen. Und letzteres ist schwer sinnvoll.



  • spielverderber schrieb:

    Hacker schrieb:

    Eig. wär es trotzdem derbst unnötig, draus 'n template zu machen.

    Nö. Aus endl ein Template zu machen ist genauso nötig/unnötig wie aus basic_ostream eines zu machen. Und letzteres ist schwer sinnvoll.

    versteh nicht was du sagen willst. Es ist also doch nötig? Oder wie?



  • Es handelt sich bei endl in diesem Fall tatsächlich um eine Funktion, nämlich std::endl<char, std::char_traits<char> >. Gemacht wird da eine außerhalb dieses Kontextes selten benutzte Form der Parameterdeduktion. Ich kann auch

    #include <iostream>
    
    template<typename T>
    void foo(T x) {
      std::cout << x << std::endl;
    }
    
    void bar(void (*func)(int)) {
      func(2);
    }
    
    int main() {
      bar(foo); // foo ist hier foo<int>
    }
    

    schreiben, muss dann aber mit hochgezogenen Augenbrauen bei den meisten Leuten rechnen, die den Code mal lesen. Man kann auch keine Zeiger auf Templates herumreichen, weil diese zur Laufzeit ja nicht mehr existieren.


Log in to reply