Template (Programmierung)
aus Wikipedia, der freien Enzyklopädie
Templates oder Schablonen sind „Programmgerüste“, die eine vom Datentyp unabhängige Programmierung ermöglichen. Templates unterstützen die generische Programmierung.
In C++ sind Schablonen von großem Nutzen, insbesondere wenn sie mit Vererbung und Überladen von Operatoren kombiniert werden. Auch die C++-Standardbibliothek stellt viele nützliche Komponenten in Form eines Template-Rahmenwerkes, zur Verfügung.
In anderen Programmiersprachen gibt es ein zu Templates ähnliches Konzept unter dem Namen „generischer Typ“.
Inhaltsverzeichnis |
[Bearbeiten] Arten von Templates
Es gibt zwei Arten von Templates in C++: Funktionstemplates und Klassentemplates.
[Bearbeiten] Funktionstemplates
Ein Funktionstemplate (auch Templatefunktion genannt) verhält sich wie eine Funktion, die in der Lage ist, Argumente verschiedener Typen entgegenzunehmen. Die C++-Standardbibliothek enthält beispielsweise das Funktionstemplate std::max(x, y)
. Es gibt entweder x
oder y
zurück, und zwar abhängig davon, welches der beiden Argumente größer ist. Es könnte etwa folgendermaßen definiert sein:
template <typename T> T max(T x, T y) { if (x < y) return y; else return x; }
Diese Schablone kann genauso aufgerufen werden wie eine Funktion:
cout << max(3, 7); // gibt 7 aus
Anhand der Argumente macht der Compiler fest, dass es sich um einen Aufruf an max(int, int)
handelt und erzeugt eine Variante der Funktion, bei der der Typ T
zu int
festgelegt wird.
Der Schablonenparameter könnte auch explizit angegeben werden. Das sähe dann so aus:
cout << max<int>(3, 7); // gibt ebenfalls 7 aus
Das Ganze funktioniert sowohl für int
also auch für std::string
oder irgendeinen anderen Typ, für den der Vergleich x < y
eine wohldefinierte Operation darstellt. Bei selbstdefinierten Typen macht man von Operator-Überladung Gebrauch, um die Bedeutung von <
für den Typ festzulegen und dadurch die Verwendung von max()
für den betreffenden Typ zu ermöglichen.
Das Beispiel für sich genommen mag nicht besonders nützlich erscheinen, im Zusammenspiel mit der C++-Standardbibliothek erschließt sich aber eine enorme Funktionalität für einen neuen Typ ganz einfach dadurch, dass man ein paar Operatoren definiert. Allein schon durch die Definition von <
wird ein Typ in die Lage versetzt, mit den Standardalgorithmen std::sort()
, std::stable_sort()
, und std::binary_search()
zusammenzuarbeiten, sowie mit Datenstrukturen wie Mengen, Stapeln, assoziativen Feldern, usw.
[Bearbeiten] Klassentemplates
Ein Klassentemplate (auch fälschlich Templateklasse genannt) wendet das gleiche Prinzip auf Klassen an. Klassentemplates werden oft zur Erstellung von generischen Containern verwendet. Beispielsweise verfügt die C++-Standardbibliothek über einen Container, der eine verkettete Liste implementiert. Um eine verkettete Liste von int zu erstellen, schreibt man einfach list<int>. Eine verkettete Liste von Objekten des Datentypes string wird zu list<string>. Mit list ist ein Satz von Standardfunktionen definiert, die immer verfügbar sind, egal was man als Argumenttyp in den spitzen Klammern angibt.
[Bearbeiten] Spezialisierung
Templates lassen sich spezialisieren, d. h. man kann Klassen- und Funktionstemplates für bestimmte Datentypen als Template-Argumente gesondert implementieren. Dies erlaubt eine effizientere Implementierung für bestimmte ausgewählte Datentypen, ohne die Schnittstelle des Templates zu verändern. Davon machen auch viele Implementierungen der C++-Standardbibliothek (beispielsweise die der GCC) Gebrauch.
[Bearbeiten] Spezialisierung bei Klassentemplates
Die Containerklasse std::vector der C++-Standardbibliothek kann für den Elementtyp bool als Bitmap implementiert werden, was Speicherplatz einsparen hilft. Auch entnimmt das Klassentemplate std::basic_string die Informationen zum Umgang mit den einzelnen Zeichen der Struktur char_traits, die für den Datentyp char und beispielsweise auch wchar_t spezialisiert ist.
Die Deklaration von Spezialisierungen ähnelt der von normalen Templates, allerdings sind die dem Schlüsselwort template folgenden spitzen Klammern leer, und dem Funktions- bzw. Klassennamen folgen die Templateparameter.
Beispiel:
template<> class vector<bool> { // Implementierung von vector als Bitmap };
[Bearbeiten] Teilweise Spezialisierung
Des Weiteren gibt es auch die so genannte teilweise Spezialisierung (auch partielle Spezialisierung genannt), die die Behandlung von Spezialfällen innerhalb eines Templates ermöglicht.
Beispiel:
template<int zeilen, int spalten> class Matrix { // Implementierung einer Matrix-Klasse }; template<int zeilen> class Matrix<zeilen, 1> { // Implementierung einer 1-spaltigen Matrix-Klasse };
Wichtig zu erwähnen ist, dass beide Klassen komplett voneinander unabhängig sind, d.h. sie erben weder Konstruktoren oder Destruktoren noch Elementfunktionen bzw. Datenelemente voneinander.
[Bearbeiten] Spezialisierung bei Funktionstemplates
Im Unterschied zu Klassentemplates sind Funktionstemplates nicht teilweise (nur vollständig) spezialisierbar. Allerdings wird von der Spezialisierung von Funktionstemplates allgemein abgeraten, da die Regeln für die Bestimmung der „am besten passenden“ Funktion sonst zu unintuitiven Ergebnissen führen können (siehe [1]).
Durch Überladen von Funktionstemplates mit anderen Funktionstemplates kann man in den meisten Fällen das Gleiche erreichen wie durch die (nicht zulässige) teilweise Spezialisierung. Falls diese Technik aus jedwedem Grund im konkreten Fall nicht anwendbar ist – z. B. wenn ein Template von Klassenmethoden spezialisiert werden soll ohne die Klassendefinition zu erweitern – so kann man auch das Problem der Spezialisierung auf ein Template einer Hilfsklasse verlagern:
class Example { private: template<typename T> struct Frobnicator { static T do_frobnicate(T param); }; public: template<typename T> T frobnicate(T param); }; template<typename T> T Example::frobnicate(T param) { // Frobnicator soll die eigentliche Arbeit verrichten return Frobnicator<T>::do_frobnicate(param); } template<typename T> T Example::Frobnicator<T>::do_frobnicate(T param) { // Standardimplementierung ... } template<> int Example::Frobnicator<int>::do_frobnicate(int param) { // ints werden auf andere Weise "frobnifiziert" return (param << 3) + (param % 7) - param + foobar; }
[Bearbeiten] Template-Templates
Mit Template-Templates werden Templates bezeichnet, die wiederum als Parameter von Templates verwendet werden. Sie stellen einen weiteren Abstraktionsmechanismus zur Verfügung. Im folgenden Beispiel wird sowohl der Typ als auch der verwendete Containers angegeben; letzterer mit Hilfe eines Template-Templates:
template<template<typename, typename> class Container, typename Type> class Example { Container<Type, std::allocator<Type> > baz; };
Beispiel zur Verwendung:
Example<std::deque, int> example;