zurück zum Artikel

Template-Instanziierung

Rainer Grimm

Template-Instanziierung ist die Erstellung einer konkreten Funktion oder einer konkreten Klasse von einem Funktions- oder Klassen-Template.

Template-Instanziierung ist das Erstellen einer konkreten Funktion oder einer konkreten Klasse von einem Funktions- oder Klassen-Template.

Der Prozess kann implizit erfolgen (vom Compiler generiert) oder explizit (vom Benutzer bereitgestellt) sein.

Template-Instanziierung

Für das Template benötigte Template-Argument generiert der Compiler automatisch. Manchmal ist es notwendig, die Template-Definitionen aus Header-Dateien zu entfernen oder die rechenintensive Template-Instanziierung zu minimieren. In diesem Fall hilft die explizite Instanziierung.

Implizite Instanziierung sollte der Default sein. Das bedeutet, dass der Compiler automatisch die konkrete Funktion oder Klasse für die angegebenen Template-Argumente erzeugt. Im Allgemeinen leitet der Compiler die Template-Argumente aus den Funktionsargumenten ab. In C++17 kann der Compiler auch die Template-Argumente für Klassen-Templates ableiten.

// implicitTemplateInstantiation.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
class MyClass{
public:
MyClass(T t) { }
std::string getType() const {
return typeid(T).name();
}
};

template<typename T>
bool isSmaller(T fir, T sec){
return fir < sec;
}

int main(){

std::cout << '\n';

std::cout << std::boolalpha;

std::vector vec{1, 2, 3, 4, 5}; // (1)
std::cout << "vec.size(): " << vec.size() << '\n';

MyClass myClass(5); // (2)
std::cout << "myClass.getType(): " << myClass.getType() << '\n';

std::cout << '\n';

std::cout << "isSmaller(5, 10): " << isSmaller(5, 10) << '\n'; // (3)
std::cout << "isSmaller<double>(5.5f, 6.5): "
<< isSmaller<double>(5.5f, 6.5) << '\n'; // (4)

std::cout << '\n';

}

(1) und (2) verwenden class template argument deductoin (CTAG). std::vector oder MyClass können ihren Typ aus ihren Konstruktorargumenten ableiten. (3) leitet auch ihr Template-Argument ab. In (4) hingegen wird das Template-Argument double explizit angegeben: isSmaller<double>(5.5f, 6.5).

Template-Instanziierung

Der Compiler erzeugt für jede implizite Template-Instanziierung eine konkrete Funktion oder Klasse. C++Insights [1] visualisiert diesen Prozess.

Dieser automatische Prozess ist sehr komfortabel, hat aber auch ein paar Nachteile.

Beide Probleme können mit expliziter Template-Instanziierung gelöst werden.

Explizite Instanziierung kennt zwei Varianten in C++: Definition und Deklaration der expliziten Instanziierung.

Beim Vergleich der Syntax , macht das Schlüsselwort extern den feinen Unterschied.

Explizite Template-Instanziierung bedeutet, dass Entwickler die Instanziierung eines Template direkt anfordern wie in folgendem einfachen Beispiel:

// explicitTemplateInstantiation.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
class MyClass{
public:
MyClass(T t) { }
std::string getType() const {
return typeid(T).name();
}
};

template<typename T>
bool isSmaller(T fir, T sec){
return fir < sec;
}

template class std::vector<int>; // (1)
template bool std::vector<double>::empty() const; // (2)

template class MyClass<int>; // (3)
template std::string MyClass<double>::getType() const; // (4)

template bool isSmaller(int, int); // (5)
template bool isSmaller<double>(double, double); // (6)

int main(){

std::cout << '\n';

std::cout << std::boolalpha;

std::vector vec{1, 2, 3, 4, 5};
std::cout << "vec.size(): " << vec.size() << '\n';

MyClass myClass(5);
std::cout << "myClass.getType(): " << myClass.getType() << '\n';

std::cout << '\n';

std::cout << "isSmaller(5, 10): "
<< isSmaller(5,10) << '\n';
std::cout << "isSmaller<double>(5.5f, 6.5): "
<< isSmaller<double>(5.5f, 6.5) << '\n';

std::cout << '\n';

}

Der Bereich (1) bis (6) ist die am interessantesten. Dank des Schlüsselworts template erfolgt eine explizite Template-Instanziierung.

Wie kann die explizite Template-Instanziierung helfen, die Definition der Templates zu verstecken?

  1. Sollte Template-Deklaration in der Header-Datei formuliert sein.
  2. Die Template-Definition sollte in der Quelldatei sein die Instanziierung des Templates am Ende der Quelldatei stattfinden.
  3. Schließlich lässt sich das Template lässt sich durch Einbinden der Header-Datei einbinden.

Hier sind drei Dateien, die diesen Prozess veranschaulichen.

// MyClass.h

#include <typeinfo>
#include <string>

template <typename T>
class MyClass{
public:
MyClass(T t) { }
std::string getType() const;
};
// MyClass.cpp

#include "MyClass.h"

template <typename T>
std::string MyClass<T>::getType() const {
return typeid(T).name();
}

template class MyClass<int>;
// mainMyClass.cpp

#include "MyClass.h"
#include <iostream>


int main() {

std::cout << '\n';

MyClass myClass(5);
std::cout << "myClass.getType(): " << myClass.getType() << '\n';

/*
MyClass myClass2(5.5);
std::cout << "myClass2.getType(): " << myClass2.getType() << '\n';
*/

std::cout << '\n';

}

Das Kompilieren und Ausführen des Programms liefert das erwartete Ergebnis.

Template-Instanziierung

Wenn ich versuche, MyClass für einen anderen Datentyp als int zu verwenden, bekomme ich einen Linker-Fehler. Folgende Fehlermeldung erhalte ich, wenn ich die auskommentierten Zeilen verwende.

Template-Instanziierung

Es ist keine Template-Instanziierung für double verfügbar.

Beim Verwenden von MyClass<int> in verschiedenen Übersetzungseinheiten wirft der Linker im Wesentlichen alle Template-Instanziierung außer einer weg. Das ist eine Verschwendung von Rechenzeit. Wer das extern-Schlüsselworts in C++11 verwendet, kann eine Definition der expliziten Instanziierung in eine Deklaration der expliziten Instanziierung transformieren:

template class MyClass<int>;        //  Definition der expliziten 
// Instanziierung
extern template class MyClass<int>; // Deklaration der expliziten
// Instanziierung

Die wichtigste Beobachtung ist, dass die zweite Zeile keine Template-Instanziierung auslöst. Das bedeutet, dass der Compiler nichts erzeugt, was der Linker gegebenenfalls wegwirft. Es gilt lediglich, sicherzustellen, dass eine Instanziierung von MyClass<int> für den Linker verfügbar ist, da sonst ein Linker-Fehle erscheint.

Nach diesem eher technischem Artikel, schreibe ich in meinem nächsten Artikel über variadische Templates ... . ( [2])


URL dieses Artikels:
https://www.heise.de/-6151298

Links in diesem Artikel:
[1] https://cppinsights.io/s/e8145723
[2] mailto:rainer@grimm-jaud.de