C++ Kurs

Funktions-/Variablen-Templates

Die Themen:

In diesem und der folgendem Kapitel werden wir uns mit Templates befassen. Ein Template ist eine Art Vorlage oder Vorschrift, die dem Compiler mitteilt, wie er etwas generieren soll. C++ kennt prinzipiell drei Arten von Templates: Funktions-Templates, Variablen-Templates und Klassen-Templates. In diesem Kapitel werden wir uns zunächst einmal die einfacheren Fälle ansehen, die Funktions- und Variablen-Templates.

Beginnen wir mit den Funktions-Templates. Ein Funktions-Template ist eine Vorlage für gleichartige Funktionen, die sich in mindestens in einem der folgenden Punkte unterscheiden:

  1. dem Returntyp der Funktion
  2. den Datentypen der Parameter
  3. den Datentypen von lokalen Variablen

Entscheidend bei dieser Aufzählung ist aber das, was nicht dort steht, nämlich was die letztendlich aus einem Funktions-Templates erzeugten Funktionen gemeinsam haben. So müssen alle Funktionen die aus einem Template generiert werden z.B. die gleiche Anzahl von Parametern besitzen, was sie von überladenen Funktionen unterscheidet. Außerdem besitzen alle Funktionen auch den gleichen Ablauf, wenigstens prinzipiell.

Vielleicht fragen Sie sich nun, was kann ich denn mit einem Funktions-Template anfangen, wenn der Ablauf immer der gleiche ist? Sehen wir uns dazu einmal die drei nachfolgenden Funktionen an. Dort werden drei Funktionen Max(...) definiert, die alle das Gleiche tun: sie liefern von zwei als Parameter übergebenen Werten den größten Wert zurück. Der Unterschied liegt hier einzig und allein in den Datentypen der Parameter. Und damit sind diese Funktionen ein geradezu klassisches Beispiel für ein Funktions-Template.

PgmHeadershort Max(short p1, short p2)
{
    return ((p1>p2)? p1 : p2);
}
long Max(long p1, long p2)
{
    return ((p1>p2)? p1 : p2);
}
float Max(float p1, float p2)
{
    return ((p1>p2)? p1 : p2);
}

Wenn Sie alles bis hierher komplett durchgearbeitet haben, so sollten Sie eine solche Template-Funktion schon aus der Standard-Bibliothek her kennen.

HinweisMit Ihrem bisherigen Wissen könnten Sie hier vielleicht auf die Idee kommen, für eine solche Funktion ein #define Makro einzusetzen. Dies wäre hier prinzipiell auch möglich, doch wird damit die Typüberprüfung der Parameter durch den Compiler umgangen. Wenn Sie anstelle einer Funktion Max(...) ein entsprechendes Makro einsetzen, so können hierbei die beiden Parameter unterschiedliche Datentypen besitzen ohne das es zu einer Fehlermeldung kommen kann.

Definition eines Funktions-Templates

Wie ein Funktions-Template prinzipiell definiert wird, soll jetzt anhand der oben aufgeführten Funktionen Max(...) aufgezeigt werden.

1. Schritt

Im ersten Schritt werden alle Funktionen bis auf eine entfernt und die Datentypen, die von Funktion zu Funktion unterschiedlich sind, durch einen beliebigen Namen ersetzt (der natürlich aber kein Schlüsselwort sein darf). Dieser beliebige Name wird auch als formaler Datentyp bezeichnet. Im nachfolgenden Beispiel wurden die Datentypen durch den Namen (Buchstaben) T ersetzt. T ist eine allgemein übliche Bezeichnung für einen solchen Template-Datentyp.

PgmHeader// Ersetzen der Datentypen
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

2. Schritt

Im zweiten Schritt müssen wir dem Compiler nun etwas unter die Arme greifen. Damit er weiß, dass T nur ein Platzhalter für einen später noch festzulegenden Datentyp ist, wird vor die Funktion noch die Anweisung

template <typename T>

gesetzt. Wohlgemerkt, der Name des formalen Datentyps T ist (fast) beliebig.

PgmHeader// Spezifikation des formalen Datentyps
template <typename T>
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

Und fertig ist die Definition eines Funktions-Templates. Beachten Sie, dass es sich hier nur um eine 'unvollständige' Definition handelt, d.h. der Compiler erzeugt noch keinen Code, da er den tatsächlichen Datentyp des formalen Datentyps natürlich zu diesem Zeitpunkt noch nicht kennt.

HinweisIn manchen Programmen werden Sie Funktions-Templates noch wie folgt definiert finden:

template <class T> T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

Hier wird anstelle des Schlüsselworts typename noch class verwendet. Dies ist aber eine veraltete Schreibweise!

Aufruf von Funktions-Templates

Ist das Funktions-Template definiert, kann die hierüber definierte Funktion wie jede normale Funktion aufgerufen werden (siehe nachfolgendes Beispiel). Denn trifft der Compiler beim Übersetzen eines Programms auf den Aufruf einer Funktion, so führt er intern folgende Schritte durch:

  1. Zuerst wird abgeprüft, ob es bereits eine Funktion gibt die exakt zu den angegebenen Datentypen beim Funktionsaufruf passt. Ist dies der Fall, wird diese Funktion aufgerufen.
  2. Gibt es keine entsprechende Funktion, so wird nach einem Funktions-Template gesucht. Gibt es ein solches, so wird der formale Datentyp durch den tatsächlichen Datentyp der Argumente beim Funktionsaufruf ersetzt und eine entsprechende Funktion durch den Compiler generiert/instanziiert. Im nachfolgenden Beispiel werden also drei Funktionen durch den Compiler erstellt, wobei der formale Datentyp T nacheinander durch die Datentypen short, long und float ersetzt wird.
  3. Gibt es weder eine Funktion noch ein Funktions-Template das zum Funktionsaufruf passt, wird eine entsprechende Fehlermeldung ausgegeben.
PgmHeader// Templatedefinition
template <typename T>
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

// Funktionsaufrufe
shortMax = Max(shortV1, shortV2);
longMax = Max(longV1, longV2);
floatMax = Max(floatV1, floatV2);

// vom Compiler instanziierte Funktionen
short Max(short p1, short p2)
{....}
long Max(long p1, long p2)
{....}
float Max(float p1, float p2)
{....}
AchtungArbeiten Sie mit getrennten Dateien für die Deklaration von Funktionen (Header-Dateien) und deren Definitionen (Quellcode-Dateien), so sollten Sie das Funktions-Template immer mit in die Header-Datei aufnehmen! Die endgültige Funktion wird ja erst beim Aufruf der Funktion aus dem Funktions-Template generiert, und dazu benötigt der Compiler den Code der Funktion.
AchtungBei der Auflösung eines Templates wird niemals eine automatische Typkonvertierung vorgenommen. Wird z.B. versucht, für den ersten Parameter der Funktion Max(...) einen short Wert anzugeben und für den zweiten Parameter einen char Wert, so müsste der Compiler den formalen Datentyp durch den Datentyp short und char ersetzen. Da aber der formale Datentyp T nur für einen bestimmten Datentyp stehen kann, meldet der Compiler hier einen Fehler! Wollen Sie trotzdem diesen Vergleich durchführen (ohne ein weiteres Funktions-Template zu spezifizieren), so müssen Sie beim Aufruf der Funktion eine entsprechende Typkonvertierung vornehmen.

Spezialisierung und Überschreiben von Funktions-Templates

Soweit, so gut. Doch was passiert nun, wenn die Funktion Max(...) mit zwei C-Strings (char-Zeigern) aufgerufen wird um die Strings miteinander zu vergleichen? Da der Compiler beim Aufruf der Funktion den formalen Datentyp durch den tatsächlichen Datentyp ersetzt, wird er die nachfolgend dargestellte Funktion generieren. Doch diese vergleicht nicht die Strings sondern nur deren Adresse!

PgmHeader// Fehlerhafter Einsatz eines Funktions-Templates
// Templatedefinition
template <typename T>
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

// Aufruf der Funktion
char *pName1, *pName2, *pMax;
.....
pMax = Max(pName1, pName2);

// Vom Compiler generierte Funktion
char* Max(char* p1, char* p2)
{
   return ((p1>p2)? p1 : p2);
}

Was also tun? Zum einen können Templates für bestimmte Datentypen spezialisiert werden. Um für einen bestimmten Datentyp ein spezielles Funktions-Template zu erstellen, wird zunächst die template-Anweisung angegeben, jetzt jedoch mit einer leeren spitzen Klammer. Der Datentyp, für den dieses Funktions-Templates verwendet werden soll, wird dann nach dem Funktionsnamen in spitzen Klammern angegeben. Ergibt sich dieser Datentyp quasi von alleine aus den Argumenten beim Aufruf der Funktion, so kann die Angabe des Datentyps auch entfallen. Somit kann das Funktions-Template auch wie folgt geschrieben werden:

PgmHeader// Allgemeine Templatedefinition
template <typename T>
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

// Spezielles Funktions-Template
// Alternative Schreibweise
// template <> const char* Max(const char *p1, const char *p2)

template<>
const char* Max<const char*>(const char *p1, const char *p2)
{
    if (strcmp(p1,p2) > 0)
        return p1;
    else
        return p2;
}

// Aufruf des speziellen Funktions-Template
pMax = Max(pName1, pName2);

Eine andere Möglichkeit besteht darin, explizit eine entsprechende Funktion vorzugeben. Wie bereits zuvor ausgeführt, prüft der Compiler vor der Generierung einer Funktion aus einem Funktions-Template zuerst immer, ob es bereits eine Funktion mit den entsprechenden Datentypen der Parameter gibt. Gibt es eine solche Funktion, so ruft er diese auf. Für unseren Fall ist dazu explizit eine Funktion Max(...) zu schreiben, die zwei Parameter vom Typ char-Zeiger besitzt und dort natürlich wie angegeben die Strings richtig vergleicht.

PgmHeader// Allgemeine Templatedefinition
template <typename T>
T Max(T p1, T p2)
{
    return ((p1>p2)? p1 : p2);
}

// Explizite Funktion zum Vergleich von C-Strings
const char* Max(const char* p1, const char* p2)
{
    if (strcmp(p1,p2) > 0)
        return p1;
    else
        return p2;
}

Lokale Daten mit formalen Datentyp

Aber formale Datentypen können nicht nur als Parameter bei Funktions-Templates eingesetzt werden. Wird innerhalb eines Funktions-Templates eine Variable mit demselben Datentyp wie einer der Parameter benötigt, so kann auch hier anstelle eines bestimmten Datentyps der formale Datentyp angegeben werden. Bei der Generierung der Funktion durch den Compiler wird auch dieser formale Datentyp wieder durch den tatsächlichen Datentyp ersetzt.

Die unten aufgeführte Funktion Swap(...) dient zum Vertauschen von Werten. Hierzu muss zuerst einer der Werte in eine lokale Variable umkopiert werden, die natürlich den gleichen Datentyp wie die übergebenen Werte besitzen muss.

PgmHeader// Funktions-Template
template <typename T>
void Swap (T& p1, T& p2)
{
    T temp = p1;
    p1= p2;
    p2 = temp;
}

// Aufruf der Funktion
short var1, var2;
....
Swap(var1, var2);

// Vom Compiler generierte Funktion
void Swap (short& p1, short& p2)
{
    short temp = p1;
    p1 = p2;
    p2 = temp;
}

Mehrere Template-Parameter

Und auch das ist möglich: Funktions-Templates können nicht nur einen sondern auch mehrere formale Datentypen besitzen. Die Anzahl der formalen Datentypen ist nicht begrenzt.

Im nachfolgenden Beispiel wird die Funktion Func(...) mit zwei Parametern mit unterschiedlichen Datentypen aufgerufen. Und damit müssen auch bei der Spezifikation des Funktions-Templates zwei formale Datentypen angegeben werden. Wie dies zu erfolgen hat, ist im Beispiel dargestellt. Beachten Sie dabei, dass beide formalen Datentypen auch innerhalb der spitzen Klammer der template-Anweisung anzugeben sind.

PgmHeader// Funktions-Template
template <typename T1, typename T2>
void Func(T1 p1, T2 p1)
{
    T1 loc1 = p1;
    T2 loc2 = p2;
    ....
}

// Aufruf der Funktion
float fVar;
char *pChar;
....
Func (fVar, pChar);

// Vom Compiler generierte Funktion
void Func(float p1, char* p2)
{
    float loc1 = p1;
    char* loc2 = p2;
    ....
}

Außer dass Funktions-Template Parameter mit formalen Datentypen besitzen, können Parameter selbstverständlich auch normale Datentypen besitzen. Im Beispiel erhält das Funktions-Template Func(...) als zweiten Parameter p2 einen int-Wert, der hier zusätzlich noch einen Defaultwert besitzt.

PgmHeadertemplate <typename T>
void Func(T p1, int p2=10)
{
    ....
}

Variadische Funktions-Templates

Sehen wir uns zunächst an, was eigentlich variadische Funktionen sind. Variadische Funktionen sind Funktionen, die eine unbestimmte Anzahl von Parametern besitzen. Auch der Einsatz einer variadischen Funktion lässt sich am besten wiederum anhand eines Beispiels erläutern.

PgmHeaderint main()
{
    short x1 = 11;         // Beliebige Variable, dient nur zur Demonstration
    // Berechne Mittelwert 4 Werten
    auto av1 = Average(1.1,2.2f,3.3,x1);
    // Berechne Mittelwert aus 7 Zahlen
    auto av2 = Average(5,6,7,x1,77,66,x1);
    // Mittelwerte ausgeben
    cout << av1 << ',' << av2 << endl;
}

Die Funktion Average() berechnet aus eine beliebigen Anzahl von Werten den Mittelwert. Da die Anzahl der Werte von Funktionsaufruf zu Funktionsaufruf variieren kann, kann hierfür keine Funktion mit einer definierten Anzahl von Parametern verwendet werden. Und eine solche Funktion mit beliebiger Anzahl von Parametern wird als variadische Funktion bezeichnet.

Hinzu kommt noch, dass auch die Datentypen der Werte von Aufruf zu Aufruf verschiedenen sein können, ja sogar innerhalb eines Aufrufs differieren können. Daraus folgt, dass die Funktion zusätzlich noch als Funktions-Template definiert werden muss. Und wie das ganz unter C++ realisiert werden kann, das sehen wir uns jetzt an.

Der Mittelwert einer Reihe von Werten ist ja bekanntlich die Summe der Werte, dividiert durch die Anzahl der Werte. Fangen wir also mit der Berechnung der Summe aus Werten mit unterschiedlichen Datentypen an. Um die Summe von zwei Werten mit beliebigen Datentypen zu berechnen, könnte z.B. das folgende Funktions-Template verwendet werden:

PgmHeader// Summe zweier Zahlen mit beliebigem Datentyp berechnen
template <typename T1, typename T2>
auto Sum(T1 val1, T2 val2) -> decltype(val1+val2)
{
    return val1+val2;
]

Der Returntyp der Funktion wird hier mittels decltype() über den Datentyp des Ergebnisses der Addition der beiden Werte bestimmt. Eine einfache Definition des Returntyps über den Datentyp eines der formalen Parameter wäre hier nicht richtig. Warum? Überlegen Sie sich einmal, welchen Datentyp der Returnwert besitzen würde, wenn ein short und und float Wert aufsummiert wird und welchen, wenn ein float und ein short Wert aufsummiert wird.

Um nun ein Funktions-Template zu definieren, dass die Summe aus einer beliebigen Anzahl von Werten mit beliebigen Datentypen berechnet, wird anstelle der beiden Template-Argumente ein sogenanntes parameter pack angegeben. Ein parameter pack wird dadurch gekennzeichnet, dass in der Template-Anweisung vor dem formalen Datentyp drei Punkte stehen.

Zusätzlich muss ein parameter pack immer als letztes Element in einer Parameterliste eines Funktions-Templates stehen, da ansonsten die einzelnen Elemente aus dem parameter pack nicht extrahiert werden können. Um einen Funktionsparameter als parameter pack zu kennzeichnen, werden ebenfalls drei Punkte angegeben, diesmal aber vor dem Parameternamen.

PgmHeader// Variadisches Funktions-Template
template <typename ...T>
auto Sum(???, T ...pack) -> decltype(???)
{
    return ???;

}

Doch wie sieht die Summenbildung in der Praxis aus?

PgmHeader// Funktions-Template ohne parameter pack
// Liefert lediglich den uebergebenen Wert zurueck

template <typename first>
auto Sum(first val) ->decltype(val)
{
    return val;
}
// Funktions-Template mit parameter pack
// Extahiert den ersten Wert aus dem uebergebenen parameter pack

template <typename first, typename ...tail>
auto Sum( first val, tail ...rest) ->decltype(val)
{
    // Addiert zum extrahierten Wert die uebrigen Werte
    // des parameter packs indem die Funktion erneut
    // aufgerufen wird (Rekursion!)

    return val + Sum(rest...);
}
// main Funktion
int main()
{
    // Summe der Werte ausgeben
    cout << Sum(1, 2.4, 100L);
}

In main() wird die Funktion Sum() mit drei Argumenten aufgerufen, worauf der Compiler aufgrund der Datentypen der Argumente die Funktion Sum(int,double,long) erzeugt und aufruft. Der Trick dabei ist nun, dass der übergebene int-Wert dem Parameter val zugewiesen wird und die restlichen Werte dem parameter pack rest. Innerhalb von Sum() wird nun erneut Sum() aufgerufen, dieses Mal jedoch mit dem verbliebenem parameter pack, d.h. es wird somit vom Compiler die Funktion Sum(double,long) erzeugt. Bei diesem zweiten Aufruf wird der nächste Wert des parameter packs, der double-Wert, dem Parameter val zugewiesen und erneut Sum() mit dem reduzierten parameter pack aufgerufen. Da der parameter pack nun nur noch den long Wert enthält, führt dies zum Aufruf des ersten Funktions-Templates. Damit ergeben sich folgende Aufrufe ({...} kennzeichnet den Inhalt des parameter pack):

Sum (1,{2.4,100L});
Sum (2.4, {100L});
Sum (100L);

Variadische Funktion-Templates sind sehr oft rekursiv, wobei als "Abbruchkriterium" der Rekursion ein Funktions-Template aufgerufen wird, welches kein parameter pack mehr erhält.

Da wir nun die Summe aus einer beliebigen Anzahl von Werten mit (fast) beliebigen Datentypen berechnen können, muss für die eigentliche Mittelwertbildung die so berechnete Summe nur noch die die Anzahl der Werte dividiert werden. Um die Anzahl der Werte in einem parameter pack zu ermitteln, wird der sizeof...(pp) Operator verwendet. Auch hier dienen wieder drei Punkte dazu, diesen Operator vom normalen sizeof() Operator zu unterscheiden. Als Argument erhält sizeof...(pp) ein parameter pack. Und damit sieht das vollständige Beispiel für die Berechnung des Mittelwerts wie folgt aus:

PgmHeader// Funktions-Template ohne parameter pack
// Liefert lediglich den uebergebenen Wert zurueck

template <typename first>
auto Sum(first val) ->decltype(val)
{
    return val;
}
// Funktions-Template mit parameter pack
// Extrahiert den ersten Wert aus dem uebergebenen parameter pack

template <typename first, typename ...tail>
auto Sum( first val, tail ...rest) ->decltype(val)
{
    // Addiert zum extrahierten Wert die uebrigen Werte
    // des parameter packs indem die Funktion erneut
    // aufgerufen wird (Rekursion!)

    return val + Sum(rest...);
}
// Funktionstemplate zur Mittelwertberechnung
// Erhält die Werte als parameter pack uebergeben

template <typename ...ppack>
auto Average(ppack ...data) ->decltype(Sum(data...)/sizeof...(data))
{
    // Uebergebe parameter pack komplett an Summen-Funktion
    // und dividiere die Summe durch Anzahl der Werte
    // im parameter pack

    return Sum(data...)/sizeof...(data);
}

// main Funktion
int main()
{
    // Summe der Werte ausgeben
    cout << Average(1,2.4, 100L);
}

Erwähnt werden soll an dieser Stelle noch, dass für den Mittelwert der gleiche Datentyp verwendet wird wie für den ersten Wert in der Aufzählung, im obigen Beispiel also ein int. Vertauschen Sie den ersten und zweiten Wert, so wird der Mittelwert als double-Wert zurückgegeben.

Externe Funktions-Templates

Wie schon erwähnt, werden Funktions-Templates erst dann instanziiert, wenn der Compiler auf den entsprechenden Funktionsaufruf triff. Wird nun in mehreren Quellcode-Dateien die gleiche Template-Funktion aufgerufen, so instanziiert der Compiler das Funktions-Template zunächst auch mehrfach. Erst beim Zusammenbinden (Linken) zu einem ausführbaren Programm werden diese mehrfachen Instanzen zusammengefasst, d.h. der Linker entfernt bis auf eine Instanz alle anderen. Um diese mehrfache Instanziierung von vorne herein zu vermeiden, kann ein Funktions-Templates auch als extern deklariert werden.

PgmHeaderTemplate-Header Datei templ.h
template <typename T>
void Swap (T v1, T2 v2)
{
    .... // Anweisungen
}

Quelldatei source1.cpp
#include "templ.h"
void Func1()
{
    int var1,var2;
    ....
    Swap(var1, var2);   // Instanziierung der Template-Funktion
    ....
}

Quelldatei source2.cpp
#include "templ.h"
extern template <typename T> Swap(T,T);
void Func2()
{
    int x1,x2;
    ....
    Swap(x1, x2);    // Keine Instanziierung der Template-Funktion
    ....
}

Beim Übersetzen der Quelldatei source1.cpp wird durch den Aufruf der Template-Funktion Swap(...) zunächst eine Instanz der Funktion Swap(int,int) angelegt. Übersetzt der Compiler anschließend die Quelldatei source2.cpp, so wird durch die Anweisung extern template <typename T> Swap(T,T); eine erneute Instanziierung der Template-Funktion verhindert.

Wenn eine Template-Funktion in einer Quelldatei als extern deklariert wird, muss die Template-Funktion selbstverständlich in einer anderen Quelldatei explizit instanziiert werden. Ansonsten erfolgt beim Linken der Dateien eine entsprechende Fehlermeldung.

Variablen-Templates

Variablen-Templates definieren einen Satz von Variablen, deren Datentypen erst beim Übersetzen des Programms festgelegt werden. Die Definition eines Variablen-Templates hat folgende Syntax: 

template <typename T> [QUALI] T NAME [= INIT];

Die Angaben in den Klammern [...] sind optional. QUALI gibt den Qualifizierer der Variablen an und kann const, volatile oder constexpr sein. NAME ist der Name der Variablen und INIT ein Initialisierungs-Ausdruck. Somit erzeugt die Anweisung

template <typename T> constexpr T data = 10.0/3.0;

zunächst nur eine Vorschrift für den Compiler, wie er die Variable data zu instantiieren hat. Die Template-Anweisung alleine erzeugt noch keine Variable (oder constexpr wie im Beispiel), genauso wie ein Funktions-Template alleine noch keine Funktion erzeugt. Erst durch eine entsprechende Instanziierung von data wird eine entsprechende Variable bzw. constexpr erzeugt.

Da der Compiler aber den für T einzusetzenden Datentyp nun nicht mehr wie bei bei den Funktions-Templates aus dem Datentyp eines Parameters bestimmen kann, muss der Datentyp bei der Instanziierung der Variable in spitzen Klammern mit angegeben werden.

auto var1 = data<double>;
auto var2 = data<int>;

Die Variable var1 hat damit den Datentyp double in den Inhalt 3.3333.. und die Variable var2 den Datentyp int und den Inhalt 3.

Aber wo ist der Einsatz eines solchen Variablen-Templates sinnvoll? Ebenso hätten wir die Datentypen der Variablen explizit angegeben und data als Konstante definieren können. Sehen Sie sich dazu zunächst einmal folgendes Programm und seine Ausgaben an:

PgmHeader
// Variablen-Template
template<typename T> constexpr T tData = T(10.0/3.0);
// Konstanter Ausdruck
constexpr auto cData = 10.0/3.0;

// Funktions-Template verwendet Variablen-Template
template<typename T>
T UseTempl(T val)
{
   return val*tData<decltype(val)>;
}
// Funktions-Template verwendet konstanten Ausdruck
template<typename T>
T UseConst(T val)
{
   return val*cData;
}

// main() Funktion
int main()
{
   cout << "UseTempl<int> : " << UseTempl(3) << endl;
   cout << "UseConst<int> : " << UseConst(3) << endl;
}
ProgrammausgabeUseTempl<int> : 9
UseConst<int> : 10

Beide Template-Funktionen multiplizieren den Ausdruck 10.0/3.0 mit dem Integerwert 3. Jedoch wird die Berechnung in der Template-Funktion UseTemp(...) als Integer-Rechnung durchgeführt und in der Template-Funktion UseConst(...) als Gleitkomma-Rechnung. Und das, obwohl beide Funktionen den gleichen Parameter erhalten. Warum? Nun, in der Funktion UseTempl(...) wird eine reine Integer-Multiplikation ausgeführt, da tData hier den Datentyp des übergebenen Parameters annimmt. In der Funktion UseConst(...) hingegeben wird immer eine Gleitkomma-Multiplikation ausgeführt, da eine Multiplikation eines Integerwertes mit einem Gleitkommawert stets eine Gleitkomma-Multiplikation erfordert.

D.h. durch Verwendung von Variablen-Templates können Sie z.B. zur Compilezeit festlegen, ob Sie eine Integer- oder Gleitkomma-Berechnung durchführen wollen. So würde ein Aufruf von UseTempl(3.0) dann ebenfalls zu einer Gleitkomma-Multiplikation führen.

Beispiel und ÜbungUnd hier geht's zum Beispiel und zur Übung.