Programmieren in C++ Templates
Inhalt Templates Funktions- und Klassen-Templates Spezialisierung von Templates Generische Klassen Einbinden von Templates Instantiierung (implizit und explizit) Templates mit Wertparameter Member-Templates Schlüsselwort typename C++11-Erweiterungen 9-2
Templates (C++ Generics) Generische Klassen und Funktionen in C++ Template = Schablone = parametrisierbarer Typ typsichere Funktionen und Klassen Quellcode vereinfachen, flexibilisieren und in der Länge reduzieren Beispiel für überladene Funktion int min( int a, int b ) { return ( a < b )? a : b; // min for ints char min( char a, char b ) { return ( a < b )? a : b; // min for chars jetzt generisch template<typename T> T min( T a, T b ) { return ( a < b )? a : b; // generic min // Einsatz int main() { int m = min(5, 7); 9-3
Funktions-Template Deklaration template < TemplateParamListe > Funktionsdefinition mit TemplateParamListe kommaseparierte Liste von Parametern, welche Typparameter oder Wertparameter sein können einfache Beispiele class TypBezeichner typename TypBezeichner, class TypBezeichner class TypBezeichner, IntegralTypBezeichner Variable template < TemplateParamListe > TypBezeichner ein beliebiger Name, der in der Funktionsdefinition als Datentyp verwendet wird sowohl Grunddatentypen als auch Klassen sind möglich Funktionsdefinition übliche Funktionsdefinition darf auch ein überladener Operator sein 9-4
Spezialisierung von Templates Beispiel Minimum von zwei Zahlen oder Zeichen bestimmen bei Zeichen soll die Gross-/Kleinschreibung nicht beachtet werden Allgemeinfall template<typename T> T min( T a, T b ) { return ( a < b )? a : b; // generic min Spezialisierung (muss nach dem Allgemeinfall folgen) template<> char min<char>(char a, char b) { if (isupper(a)) a = _tolower(a); if (isupper(b)) b = _tolower(b); return ( a < b )? a : b; 9-5
Klassen-Templates Deklaration template < TemplateParamListe > Klassendefinition mit TemplateParamListe: siehe Funktions-Templates Beispiele von generische Klassen template<class T> class Vector { ; T* m_array; template<typename T=char> class String { ; T* m_string; // default Zeichentyp ist char 9-6
Templates mit Wert-Parameter Beispiel: statisches Array mit variabler Länge template<class T, int i> class Array { T m_array[i]; public: Array() { memset(m_array, 0, i*sizeof(t)); const T& operator[ ](int pos) const { return m_array[pos]; T& operator[ ](int pos) { return m_array[pos]; void print() const { int l = 0; cout << '['; if (i > 0) cout << m_array[l++]; while(l < i) cout << ',' << m_array[l++]; cout << ']' << endl; ; 9-7
Rekursive Templates Idee aus Templates mit Wert-Parametern und aus spezialisierten Templates können rekursive Templates mit Verankerung (Spezialisierung) erzeugt werden Beispiel template<int n> struct Zweihoch { ; enum { Wert = 2*Zweihoch<n - 1>::Wert ; template<> struct Zweihoch<0> { ; enum { Wert = 1 ; int main() { cout << Zweihoch<11>::Wert << endl; 9-8
Einbinden von Templates Klassische Aufteilung in Spezifikation und Implementierung macht bei Templates nur bedingt Sinn, weil die kompilierte Objektdatei eines Templates keinen Code und keine Daten enthält Beispiel h-file: template<typename T> T max(t a, T b); // Prototyp cpp-file: Linker: #include <h-file> template<typename T> T max(t a, T b) { return ( a > b )? a : b; unresolved external symbol "int cdecl max<int>(int,int)" Template-Dateien Templates werden oft in eigene Dateien separiert oft verwendete Dateiendungen.h: ursprünglich für C-Header-Dateien gedacht.hpp: ok, ursprünglich für C++-Header, die in C nicht verwendet werden können.cpp: ok, include einer cpp-datei ist vielleicht etwas ungewohnt.t: wird nicht von allen Compilern als C++-Datei erkannt 9-9
Alias Templates (C++11) Idee: Kurzschreibweisen ermöglichen generische Typen können unter Umständen sehr lange Bezeichner erhalten, vor allem dann, wenn mehrere generische Parameter verschachtelt werden Beispiel Array<vector<unsigned long long int>*, 50> myarray; typedef kann dann verwendet werden, wenn alle Template-Parameter voll spezifiziert worden sind Beispiel typedef Array<vector<unsigned long long int>*, 50> AV50; using (C++11) muss dann verwendet werden, wenn nicht alle Template-Parameter spezifiziert sind Beispiel template<typename T> using A50 = Array<T, 50> 9-10
Instantiierung Template Deklaration deklariert eine Funktion oder Klasse nicht vollständig definiert nur eine syntaktische Schablone für eine Funktion oder Klasse Instantiierte Funktion oder Klasse durch Instantiierung wird aus einem Template eine vollständige Funktion oder Klasse implizite (automatische) und explizite Instantiierung werden unterschieden Beispiel einer generischen Klasse template <typename T> class A { T m_t; public: A(T t): m_t(t) { void f(t t); ; // Implizite Instantiierung int main() { A<double> a(3.5); 9-11
Explizite Instantiierung Idee instantiiert ein Template ohne konkreten Gebrauch der Klasse oder Funktion, d.h. z.b. ohne die Erzeugung einer Instanz der Klasse die explizite Instantiierung generiert nur spezifizierten Code, d.h. die Typparameter sind durch konkrete Werte ersetzt worden kann die Kompilation eines grossen Projektes wesentlich beschleunigen, wenn die expliziten Instantiierungen nur an wenigen zentralen Orten vorgenommen wird Syntax template class TypBezeichner < TypBezeichner >; template TypBezeichner < TypBezeichner > ::Methode; template TypBezeichner TypBezeichner::Methode < TypBezeichner > (ParameterListe); Beispiele template class Array<double, 20>; template Array<double, 10>::Array(); extern template class Array<int, 30>; // ganze Klasse wird instantiiert // nur Konstruktor wird instantiiert // C++11: externe Instantiierung ist // bereits vorhanden 9-12
Member-Templates Idee Funktions-Templates können auch auf Instanzmethoden einer (generischen) Klasse angewendet werden Beispiel template<typename T, int i> class Array { T m_array[i]; public: Array() { memset(m_array, 0, i*sizeof(t)); ; template<typename E> bool lessthan(e a[], int size) { int k = 0, m = min(i, size); while(k < m && m_array[k] < a[k]) k++; return k < m; 9-13
Schlüsselwort typename Zweck mit dem Schlüsselwort typename wird dem Compiler mitgeteilt, dass ein unbekannter Bezeichner ein Typ ist Einsatz nur im Zusammenhang mit Template-Definitionen zwischen den spitzen Klammern: gleiche Bedeutung wie class typename muss verwendet werden, wenn der unbekannte Bezeichner ein vom Template abhängiger qualifizierter Name ist Beispiel template<typename T> class List { class Element { Element *m_next; T m_data; ; private: Element m_root; ; template<class E> class Log { typename E::Element m_el; ; 9-14
C++11-Erweiterungen Statische Zusicherungen assert überprüft die Zusicherung zur Laufzeit für Templates möchte man aber schon zur Kompilationszeit gewisse Zusicherungen überprüfen können static_assert template<typename T> class MyInt { ; static_assert(sizeof(int) <= sizeof(t), "T is not big enough"); Funktionale Schreibweise template<class LHS, class RHS> auto plus(lhs& l, RHS& r) -> decltype(l+r) { return l + r; Templates mit beliebiger Anzahl Argumente (variadic templates) template<typename Arguments> class C; 9-15
Variadic Template Beispiel (C++11) // spezialisiertes Funktionstemplate template<typename T> void kommaliste(t value) { cout << value << endl; ; // Funktion mit beliebiger Anzahl Parameter beliebigen Typs template<typename First, typename Rest > void kommaliste(first first, Rest rest) { cout << first<< ", "; kommaliste(rest ); // wird auf alle Elemente des Rest angewendet ; int main() { kommaliste(42, "hallo", 2.3, 'a'); 9-16