Templates

Ein wenig haben Templates in diesem Tutorial schon eine Rolle gespielt, sie sind heute wohl die wichtigste Eigenschaft von C++, welche C++ zu anderen Sprachen klar abgrenzt. Ursprünglich hat man Templates vorallem als Vorlagen gesehen, so dass man eine Listenklasse nur einmal implementieren, und dann mit verschiedenen Typen nutzen konnte. Diese Technik findet sich auch z.b. in Java als Generics. Die Implementation von Templates in C++ geht aber etwas weiter, und unterscheidet sich in einigen wichtigen Punkten. Das wichtigste ist wohl, das Templates nur zur Compilezeit existieren, zur Laufzeit lassen sich keine neuen Templates initialisieren, oder ergänzen.

 

Innerhalb der Compilierung sind C++ Templates aber turingcomplete. Man kann also Werte zur Compilezeit berechnen, sogar ganze Programme. Ein Template könnte so zum Beispiel berechnen, ob eine als Parameter übergebene Zahl eine Primzahl ist. So ergibt isprime<42>::value false, aber isprime<23>::value true.

In der C++ Standard Bibliothek werden Templates für Container Klassen (Listen, Maps etc.), aber auch um verschiedene Algorithmen auf diesen auszuführen. Dazu mehr im nächsten Kapitel.

C++ kennt 3 verschiedene Formen von Templates:

  • Funktionstemplates

    • Eine Funktion als Template

  • Klassentemplates

    • Eine Klasse wird als Template zur Vorlage

  • Methodentemplates

    • Eine einzelne Methode einer Klasse wird als Template implementiert


Für alle 3 Formen ein kurzes Beispiel.

Als Funktionstemplate könnte man eine Funktion add implementieren, welche dann jeweils die übergebenen Parameter addiert:

 

template<class T>
T add(const T a, const T b)
{
    return a + b;
}
 

Hierbei wird zur Compilezeit die Methode für die entsprechenden Aufrufe generiert. Die Parameterliste eines Templates steht immer nach dem Schlüsselwort template in spitzen Klammern.

Ein einfaches Beispiel für ein Klassentemplate:

 

template<class Holding>
class Holder
{
    Holding m_value;
public:
    Holder(const Holding& value):m_value(value)
    {}
    Holding getValue()const
    {
        return m_value;
    }
    void setValue(const Holding& value)
    {
        m_value = value;
    }
};
 

Dieses Template kann, wenn für einen Typen instanziiert, einen Wert dieses Typen kapseln.

Dieses Beispiel ist natürlich nur eingeschränkt sinnvoll, zeigt aber den Aufbau eines Klassentemplates. Methodentemplates können in Templates oder normalen Klassen vorkommen, als Beispiel dient ein Template:

 

template<class MyContainer>
class MyData
{
    MyContainer m_data;
public:
    // Konstruktoren etc.
    template<class Container>
    void copyToContainer(Container& c)
    {
        MyContainer::iterator it = m_data.begin(),end = m_data.end();
        for(;it != end; ++it)
            c.insert(it);
    }
};
 

MyData wird mit einem Container initialisiert, welcher die Daten der Klasse verwaltet. Für manche Aufgaben aber möchte man die Daten in einen anderen ContainerTypen konvertieren, dies geschieht im copyToContainer Methodentemplate.