In der Praxis tritt oft das Problem auf, eine Funktion mehrfach programmieren zu müssen, um ein und denselben Algorithmus auf verschiedene Typen von Argumenten anzuwenden.
Ein typisches Beispiel ist das Aussuchen des kleineren Werts
aus zwei Argumenten. Gegebenenfalls muß man zu diesem Zweck
mehrere Funktionen definieren, die jeweils int
-,
unsigned int
-,
double
-Argumente usw. erhalten und
entsprechende Rückgabewerte liefern.
Neben dem Aufwand, alle Versionen auszuformulieren, hat man dann noch das Problem, die jeweils passende Funktion mit dem richtigen Namen aufzurufen. (Letzteres ließe sich gegebenenfalls durch Überladen lösen.)
Schön wäre es, wie in FORTRAN generische Funktionen zu haben, die sich im Typ der Argumente unterscheiden und dementsprechend passende Ergebnisse liefern, aber trotzdem alle gleich heißen und zudem nicht mehrfach definiert werden müssen (wie in Abschnitt Überladen von Funktionen gezeigt), sondern nur einmal -unabhängig vom tatsächlichen Typ- hingeschrieben werden. Der Compiler muß dann die passende Version auswählen.
In C kann man sich ein Makro definieren, um den kleineren zweier Werte zu finden:
// Time-stamp: "(14.11.01 20:39) min.c [Klaus Wachtler (aw38)]" #include <stdio.h> #define MIN(a,b) ( (a)<(b) ? (a) : (b) ) int main() { int i = 5, j = 7; double d = 3.14, e = 2.7182818; printf( "MIN( i, j ) ist %d\n", MIN( i, j ) ); printf( "MIN( d, e ) ist %f\n", MIN( d, e ) ); return 0; }Damit hat man so etwas ähnliches wie eine generische Funktion definiert; leider ist es aber keine Funktion, auch wenn MIN in vielen Fällen sehr ähnlich verwendet werden kann.
Diese Definition hat gegenüber einer echten Funktion Vor- und Nachteile:
MIN(i++,j++)
unübersichtliche Seiteneffekte auftreten,
weil einer der beiden Parameter zweifach ausgewertet wird.
C++ bietet nun die Möglichkeit, generische Funktionen (wie sie zumindest festverdrahtet von FORTRAN bekannt sind), selbst zu definieren.
Das Gegenstück zum MIN
-Makro könnte in C++ so aussehen:
// Time-stamp: "(15.11.01 16:10) min.cpp [Klaus Wachtler (aw38)]" #include <iostream> using namespace std; template<class T> T min( T a, T b ) { return a<b ? a : b; } int main() { int i = 5, j = 7; double d = 3.14, e = 2.7182818; cout << "min( i, j ) ist " << min( i, j ) << "\n"; cout << "min( d, e ) ist " << min( d, e ) << "\n"; return 0; }Hier ist
min
als Schablone für Funktionen definiert,
die alle den gleichen Namen
(min
) haben und sich im Typ der Argumente und des Rückgabewerts
unterscheiden.
Bei jeder Verwendung in einem konkreten Zusammenhang setzt der Compiler
einen Aufruf der passend generierten tatsächlichen Funktion ein.
So werden die gefährlichen Nebeneffekte von Makros vermieden. Bei
Bedarf kann man sich auch Definitionen der min
-Funktion
schreiben, die keine skalaren Daten vergleichen, sondern beispielsweise
eigene Datentypen wie Listen oder Strings.
Durch das Schlüsselwort inline
wird die Funktion
-sofern sie nicht zu umfangreich ist- gar nicht als eigene Funktion
zur Laufzeit aufgerufen, sondern der Compiler erzeugt den entsprechenden
Code an Ort und Stelle anstatt eines Funktionsaufrufs. Dadurch wird
-genau wie in der Version als Makro- das Programm schneller, da zur
Laufzeit keine Parameter angelegt werden müssen. Trotzdem sorgt der Compiler
dafür, daß keine Seiteneffekte auftreten, beispielsweise durch die erwähnte
mehrfache Auswertung von Parametern.
Die Verwendung solcher Schablonen ist nicht auf einen freien Parameter beschränkt, sondern man kann mehrere (mit Kommata getrennt) in die Dreiecksklammern schreiben. Weiterhin müssen diese Parameter gar keine Datentypen sein, sondern können auch Werte sein.
Genau genommen sind folgende Templateparameter zulässig:
Da alle Parameter, die kein Typ sind, als Konstanten gelten, ist es nicht zulässig, sie im Template ändern zu wollen.
Ebenso wie normale Funktionen lassen sich Templatefunktionen überladen.
Zu beachten ist, daß sich die verschiedenen generierten Funktionen auch in den Aufrufparametern unterscheiden müssen, damit der Compiler die erzeugten überladenen Funktionen auseinanderhalten kann. Deshalb müssen sich die Templateparameter auf die Typen der Parameterliste auswirken. Ohnehin gilt, daß alle Parameter der Templatedefinition auch verwendet werden müssen.