Einführung in die Programmierung mit Templates



  • Hallo,

    hab mal im Standard nachgesehen und bin auf folgende Punkte gestossen:

    14.5.5 Function templates

    A function template can be overloaded with other function templates and with normal (non-template) func-
    tions. A normal function is not related to a function template (i.e., it is never considered to be a specializa-
    tion), even if it has the same name and type as a potentially generated function template specialization. 130)

    __________________
    130) That is, declarations of non-template functions do not merely guide overload resolution of template functions with the same name.
    If such a non-template function is used in a program, it must be defined; it will not be implicitly instantiated using the function tem-
    plate definition.

    14.5.5.1 Function template overloading
    It is possible to overload function templates so that two different function template specializations have the
    same type. [Example:

    // file1.c                               // file2.c
           template<class T>                        template<class T>
                void f(T*);                                void f(T);
           void g(int* p) {                         void h(int* p) {
                f(p); // call                              f(p); // call
                      // f<int>(int*)                            // f<int*>(int*)
           }                                        }
    

    -end example]
    Such specializations are distinct functions and do not violate the one definition rule (3.2).
    The signature of a function template specialization consists of the signature of the function template and of
    the actual template arguments (whether explicitly specified or deduced).

    14.7 Template instantiation and specialization

    An explicit specialization may be declared for a function template, a class template, a member of a class
    template or a member template. An explicit specialization declaration is introduced by template<>.

    Warum nennt man es dann im Kommentar im Code und im Absatz darüber "Spezialisierung"?

    In der Überschrift steht klar Überladung (Spezialisierung), es muss wohl ein Flüchtigkeitsfehler gewesen sein.

    Der Standard kennt einen Unterschied zwischen Überladung und Spezialisierung, ich finde, den sollte man auch kennzeichnen und die Begriffe nicht synonym verwenden. Um so verwirrender ist der von dir zitierte Satz ja auch noch, wenn gleich darauf gesagt wird, daß das illegal wäre und man ein "template<>" davor setzen muss, was die Funktion templatisieren würde und somit eine echte Template-Spezialisierung hervorbringen würde.

    Nun, ich anerkenne, dass es einen Unterschied zw. der Überladung und der *echten* Spezialisierung gibt. Dieser Unterschied wird im Artikel tatsächlich nicht ausreichend dargestellt.

    Ich werde den betreffenden Abschnitt abändern, sobald ich Zeit dafür finde. Okay?

    MfG

    GPC



  • GPC schrieb:

    hab mal im Standard nachgesehen und bin auf folgende Punkte gestossen:

    14.5 habe ich doch auch zitiert :p

    GPC schrieb:

    Okay?

    Du musst mit mir nicht verhandeln, es ist dein Artikel. Ich wollte lediglich Fehler (oder was meiner Meinung nach welche sind) aufzeigen 🙂



  • tommie-lie schrieb:

    GPC schrieb:

    hab mal im Standard nachgesehen und bin auf folgende Punkte gestossen:

    14.5 habe ich doch auch zitiert :p

    Joah, aber der war mir zu allgemein, ich fand die Beschreibung mit 14.5.5 und 14.5.5.1 exakter und aufschlussreicher...

    GPC schrieb:

    Okay?

    Du musst mit mir nicht verhandeln, es ist dein Artikel. Ich wollte lediglich Fehler (oder was meiner Meinung nach welche sind) aufzeigen 🙂

    Es sind Fehler, daher werde ich sie auch korrigieren. Jeder will hier schließlich gute Arbeit abliefern, wenn auch verspätet^^
    Wir sind sogar auf aufmerksame Leser angewiesen, wenn wir Fehler finden wollen, die die interne Revision überlebt haben. 🙂

    MfG

    GPC



  • Okay, Abschnitt umgeschrieben 🙂



  • GPC schrieb:

    Okay, Abschnitt umgeschrieben 🙂

    Besser!

    Aber nur um bei den vorhandenen Beispielen zu bleiben und anhand eines Beispiels alles zu erklären könnte man die Überladung von minimum<>() auch für C-Strings zeigen.
    Also nach dem Motto:

    Eine echte Spezialisierung unseres minimum<>()-Templates für Strings würde demnach also so aussehen:

    template<>
    inline const char *&minimum(const char *&a, const char *&b) {
      return ((strcmp(a, b) < 0) ? b : a);
    };
    


  • Hem, ich dachte ich bring mal was anderes als das ausgelutschte minimum... aber in diesem Kontext verdeutlicht es die Spezialisierung wohl besser. 🙂



  • Sorry, aber mein Code war auch noch falsch.
    Dein Template verlangt "const T &", also eine konstante Referenz. Meine "char *"-Spezialisierung gibt ihm aber eine nicht-konstante Referenz auf einen Pointer auf einen const char. Sowas passiert, wenn man mal eben schnell ohne Compiler ein Template zusammensetzt und noch nicht ganz wach ist...
    Das Template mit Inline mit const und allem drum und dran wird für einen Anfänger in dieser Form vielleicht etwas haarig, aber richtig (und mit Erklärung) wäre die Spezialisierung für "char *" folgendermaßen:

    template<>
    inline const char *const &minimum(const char *const &a, const char *const &b) {
      return ((strcmp(a, b) < 0) ? b : a);
    };
    

    Man könnte zwar Denken, daß ein "const char *&a" ausreichen sollte, wenn man für T einfach stupide ein "char *" einsetzt, aber so denkt der C++-Standard nicht. Die Typen im generischen Template sind "const T&", was einer konstanten Referenz auf T entspricht. Wollen wir das gleiche mit einem Pointer erreichen, darf nicht das Ziel des Pointers const sein, sondern die Referenz. Somit würde eine Template-Spezialisierung für "char *" folgendermaßen aussehen:

    [cpp]template<>
    inline char *const &minimum(char *const &a, char *const &b) {
      return ((strcmp(a, b) < 0) ? b : a);
    };
    

    Die Typen entsprechen einer konstanten Referenz auf einen Zeiger auf einen char. Natürlich kann man dadurch den String selbst noch verändern, um also auch noch den char konstant zu deklarieren, benötigen wir das zweite const, was insgesamt den Typ "const char *const &" ergibt. Die Spezialisierung ist nun "template<const char *>minimum(), also eine Spezialisierung für "const char *" und nicht mehr nur für "char *".

    Ist zwar sehr verwirrend, aber durch "const T &" legst du ja schon relativ verwirrungsträchtig vor 😉

    Ist ein wenig ins Unreine geschrieben, die perfekte Integration in deinen Artikel überlasse ich dir (hängt auch davon ab, wie sehr du deinen Artikel über Templates auch die "const correctness" beleuchten lassen willst).



  • Sorry, aber mein Code war auch noch falsch.

    Tjo, mir ist's in der Eile auch nicht aufgefallen, das kommt von Copy & Paste^^

    Deine Erklärung ist gut, jedoch befürchte ich, dass es an der Stelle etwas zu viel wird, zum Einen das Template und dann noch die const-correctness (die ich übrigens in meinem Pointer-Artikel erläutert habe 🙂 ). Daher bin ich wieder auf das einfache Beispiel umgestiegen.

    Du bist ziemlich fit, was C++ angeht, besonders Templates... darf ich dich hierauf aufmerksam machen? Na wie sieht's aus, hättest du Zeit und Lust für den Artikel?

    MfG

    GPC



  • GPC schrieb:

    jedoch befürchte ich, dass es an der Stelle etwas zu viel wird

    Ja, ich auch.

    GPC schrieb:

    zum Einen das Template und dann noch die const-correctness (die ich übrigens in meinem Pointer-Artikel erläutert habe 🙂 ).

    Wenn die Geschichte mit Referenzen auf Konstanten in dem Artikel erklärt wird, würde ich für jeden, der wissen will, was "const char *const &" bedeutet, dorthin verlinken. Früher oder später wird man ohnehin const-correctness kapieren müssen 😉

    GPC schrieb:

    Du bist ziemlich fit, was C++ angeht, besonders Templates...

    Geht so. Ich scheine nur der einzige zu sein, der bei Diskussionen "C++ gegen den Rest der Welt" immer der Meinung ist, C++ habe eine einfach zu verstehende Syntax. 😃

    GPC schrieb:

    darf ich dich hierauf aufmerksam machen? Na wie sieht's aus, hättest du Zeit und Lust für den Artikel?

    Habe ich so noch nicht mit gearbeitet, aber die Beispiele im Wikipedia-Artikel haben was... (sind allerdings in dieser Formatierung ohne Highlighting ziemlich unleserlich ;)). Außerdem habe ich nicht vor, mir auch noch großartig Bücher zu kaufen. Und ich müsste mir erstmal einen Algorithmus aus den Fingern saugen, der vom Verständnis her einfach ist und der sich auch syntaktisch nicht allzu verwirrend in C++-Templates gießen lässt. Wie gesagt, schon für das Potenzierungs-Beispiel von Wikipedia brauchte ich einen Syntaxhighlighter und zweimaliges Lesen (die Fakultät im englischen Aritkel war da deutlich einfacher).
    Vielleicht kriege ich ja passende Beispiele serviert, wenn ich studiere.
    Aber ich biete mich gerne zum Gegenlesen an, wenn einer einen Artikel hat.



  • Hi,

    hab das gerade mal nen bisschen ausprobiert, aber folgendes verstehe ich nicht:

    #ifndef MY_CLASS_H
    #define MY_CLASS_H
    
    #include <iostream>
    
    template <typename T>
    class MyClass
    {
    	private:
    		T m_Element;
    
    	public:
    		MyClass( void ) { ; }
    		~MyClass( void ) { ; }
    
    		void setElement( T NewElement ) 
    		{ 
    			std::cout << "[Typ]NewElement: ???" << std::endl;
    			m_Element = NewElement; 
    		}
    
    		//template< > // Wie funktioniert das nun!?
    		void setElement( int NewElement ) 
    		{
    			std::cout << "[Typ]NewElement: int" << std::endl;
    			m_Element = NewElement; 
    		}
    
    		T getElement( void ) { return m_Element; }
    
    		void PrintElement( void ) 
    		{
    			std::cout << "Element: " << m_Element << std::endl; 
    		}
    };
    
    #endif /* MY_CLASS_H */
    

    Bei mir compiliert der Code, aber so solls ja glaube nicht gemacht werden, ohne das template < >?

    Danke

    Gruß



  • Was willst du überhaupt machen? Die Methode setElement für int spezialisieren?



  • Richtig. Genau das. Ich weiß, dass das schwachsinnig ist, aber irgendwie muss ich das ja ausprobieren nur klappt das nicht...

    /edit by GPC: nick gekürzt



  • Na ja, mir fehlt etwas der konkrete Anwendungsfall, aber wenn du z.B. den Code ausführst:

    //hier dein Code
    
    int main() {
      MyClass<float> fc;
      fc.setElement(2.5f);
      fc.setElement(2);
    }
    

    dann gibt's folgende Ausgabe:

    gpc@desktop:~$ g++ -o main main.cpp
    gpc@desktop:~$ ./main
    [Typ]NewElement: ???
    [Typ]NewElement: int
    

    Btw. Bitte verwende einen kürzeren Nick. Danke 🙂

    MfG

    GPC



  • So habe ich aber keine Spezialisierung sondern nur eine normale Überladung. Kann doch nicht richtig sein?



  • Stimmt, das ist Überladung. Da du aber ein Klassentemplate hast, kannst du auch nur das spezialisieren (partiell oder vollständig). D.h. entweder die Überladung oder du machst es so:

    //Generisch:
    template <typename T>
    struct Foo {
      T bar;
    
      void set_bar(const T &x) { bar = x; }
      const T& get_bar() const { return bar; }
    };
    
    //Für ints:
    template <>
    struct Foo<int> {
      //hier spezialisierte Versionen der Methoden von oben
    };
    

    Damit hast du allerdings die Klasse spezialisiert.

    Template-Methoden spezialisieren geht auch, aber dann brauchst du template-Methoden.



  • btw, SFINAE steht für substitution failure is not an error



  • Magst du evtl noch die Syntax für Template-friend-Deklarationen mit reinnehmen? Solche speziellen Sachen vergess ich immer, brauch's ja auch fast nie 🙂 edit: Hier meine ich template<typename[,...]> friend class XYZ;
    Achso, und magst du vielleicht auch noch mal explizit reinschreiben, dass es keine partiell spezialisierten Funktionen gibt? Hatte neulich vergessen, dass es nur mit Klassen geht und mir 'nen Wolf gesucht 😃



  • Kann ich machen, aber nach der Klausurphase...


Anmelden zum Antworten