zurück zum Artikel

Template-Spezialisierung: Mehr Details zu Klassen-Templates

Rainer Grimm

Nachdem ich in meinem letzten Beitrag die Grundlagen zur Template-Spezialisierung vorgestellt habe, tauche ich heute tiefer ein. Ich möchte die partielle und vollstÀndige Spezialisierung eines Klassen-Templates als Compilezeit if vorstellen.

Nachdem ich in meinem letzten Beitrag [1] die Grundlagen zur Template-Spezialisierung vorgestellt habe, tauche ich heute tiefer ein. Ich möchte die partielle und vollstÀndige Spezialisierung eines Klassen-Templates als Compilezeit if vorstellen.

Template Spezialisierung - Mehr Details zu Klassen-Templates

Zu meinen EinfĂŒhrungen in die Template-Spezialisierung habe ich ein paar Ă€hnliche Fragen erhalten, darunter: Wie kann man entscheiden, ob ein Datentyp einen gegebenen Typ besitzt oder zwei Typen gleich sind? Die Beantwortung dieser Fragen ist einfacher als es scheinen mag und hilft mir, weitere Theorien ĂŒber die Spezialisierung von Klassen-Templates zu prĂ€sentieren. FĂŒr meine Antwort implementiere ich vereinfachte Versionen von std::is_same [2] und std::remove_reference [3]. Die in diesem Post vorgestellten Techniken sind eine Anwendung der Spezialisierung von Klassen-Templates und stellen ein Compilezeit if dar.

std::is_same ist eine Funktion aus der type-traits-Bibliothek. Sie gibt std::true_type [4] zurĂŒck, wenn beide Typen gleich sind, andernfalls gibt sie std::false_type zurĂŒck. Der Einfachheit halber gebe ich in meiner Implementierung true oder false zurĂŒck.

// isSame.cpp

#include <iostream>

template<typename T, typename U> // (1)
struct isSame {
static constexpr bool value = false;
};

template<typename T> // (2)
struct isSame<T, T> {
static constexpr bool value = true;
};

int main() {

std::cout << '\n';

std::cout << std::boolalpha;
// (3)
std::cout << "isSame<int, int>::value: " << isSame<int, int>::value << '\n';
std::cout << "isSame<int, int&>::value: " << isSame<int, int&>::value << '\n';


int a(2011);
int& b(a); // (4)
std::cout << "isSame<decltype(a), decltype(b)>::value " <<
isSame<decltype(a), decltype(b)>::value << '\n';

std::cout << '\n';

}

Das primĂ€re Template (1) gibt als Default false zurĂŒck, wenn du nach dem Wert fragst. Im Gegensatz dazu gibt die partielle Spezialisierung (2), die verwendet wird, wenn beide Typen gleich sind, true zurĂŒck. Das Klassen-Template isSame lĂ€sst sich auf Datentypen (3) und, dank decltype, auf Werte (4) anwenden. Der folgende Screenshot zeigt die Ausgabe des Programms:

Template Spezialisierung - Mehr Details zu Klassen-Templates

Du ahnst es wohl schon? Das Klassen-Template isSame ist ein Beispiel fĂŒr Template-Metaprogrammierung. Nun muss ich einen kleinen Umweg machen und ein paar Worte ĂŒber Meta verlieren.

Zur Laufzeit verwenden wir Daten und Funktionen. Zur Compilezeit verwenden wir Metadaten und Metafunktionen. Ganz einfach, es heißt Meta, da wir Metaprogrammierung umsetzen. Was sind Metadaten oder Metafunktionen? Hier ist die erste Definition:

Lass mich die Begriffe Metadaten und Metafunktion nÀher erlÀutern.

Metadaten beinhalten drei EntitÀten:

In der Metafunktion isSame habe ich nur Datentypen verwendet.

Datentypen wie das Klassen-Template isSame werden in der Template-Metaprogrammierung verwendet, um Funktionen zu simulieren. Basierend auf meiner Definition von Metafunktionen, können constexpr-Funktionen auch zur Compile Time ausgefĂŒhrt werden und sind somit auch Metafunktionen.

Eine Metafunktion kann nicht nur einen Wert, sie kann auch einen Datentyp zurĂŒckgeben. Per Konvention gibt eine Metafunktion einen Wert mittels ::value und einen Datentyp mittels ::type zurĂŒck. Die folgende Metafunktion removeReference gibt einen Datentyp als Ergebnis zurĂŒck.

// removeReference.cpp

#include <iostream>
#include <utility>

template<typename T, typename U>
struct isSame {
static constexpr bool value = false;
};

template<typename T>
struct isSame<T, T> {
static constexpr bool value = true;
};

template<typename T> // (1)
struct removeReference {
using type = T;
};

template<typename T> // (2)
struct removeReference<T&> {
using type = T;
};

template<typename T> // (3)
struct removeReference<T&&> {
using type = T;
};

int main() {

std::cout << '\n';

std::cout << std::boolalpha;
// (4)
std::cout << "isSame<int, removeReference<int>::type>::value: " <<
isSame<int, removeReference<int>::type>::value << '\n';

std::cout << "isSame<int, removeReference<int&>::type>::value: " <<
isSame<int, removeReference<int&>::type>::value << '\n';

std::cout << "isSame<int, removeReference<int&&>::type>::value: " <<
isSame<int, removeReference<int&&>::type>::value << '\n';


// (5)

int a(2011);
int& b(a);
std::cout << "isSame<int, removeReference<decltype(a)>::type>::value: " <<
isSame<int, removeReference<decltype(a)>::type>::value << '\n';

std::cout << "isSame<int, removeReference<decltype(b)>::type>::value: " <<
isSame<int, removeReference<decltype(b)>::type>::value << '\n';

std::cout << "isSame<int, removeReference<decltype(std::move(a))>::type>::value: " <<
isSame<int, removeReference<decltype(std::move(a))>::type>::value << '\n';

std::cout << '\n';

}

In diesem Beispiel wende ich die zuvor definierte Metafunktion isSame und die Metafunktion removeReference an. Das primĂ€re Template removeReference (1) gibt T mittels des Bezeichners type zurĂŒck. Die partiellen Spezialisierungen fĂŒr die lvalue-Referenz (2) und die rvalue-Referenz geben ebenfalls T zurĂŒck, indem sie die Referenzen von ihrem Template-Parameter entfernen. Wie zuvor lĂ€sst sich die Metafunktion removeReference mit Typen (4) und, dank decltype, mit Werten (5) verwenden. decltype(a) gibt einen Wert, decltype(b) gibt eine lvalue-Referenz und decltype(std::move(a)) gibt eine rvalue-Referenz zurĂŒck.

Zum Abschluss folgt hier die Ausgabe des Programms:

Template Spezialisierung - Mehr Details zu Klassen-Templates

Es gibt eine Falle, in die ich bereits getappt bin. Wenn eine Memberfunktion eines voll spezialisierten Klassen-Templates außerhalb der Klasse definiert wird, darf nicht template<> verwendet werden.

Das folgende Programm zeigt das Klassen-Template Matrix, das eine partielle und eine vollstÀndige Spezialisierung besitzt.

// specializationExtern.cpp

#include <cstddef>
#include <iostream>

template <typename T, std::size_t Line, std::size_t Column> // (1)
struct Matrix;

template <typename T> // (2)
struct Matrix<T, 3, 3>{
int numberOfElements() const;
};

template <typename T>
int Matrix<T, 3, 3>::numberOfElements() const {
return 3 * 3;
};

template <> // (3)
struct Matrix<int, 4, 4>{
int numberOfElements() const;
};

// template <> // (4)
int Matrix<int, 4, 4>::numberOfElements() const {
return 4 * 4;
};

int main() {

std::cout << '\n';

Matrix<double, 3, 3> mat1; // (5)
std::cout << "mat1.numberOfElements(): " << mat1.numberOfElements() << '\n';

Matrix<int, 4, 4> mat2; // (6)
std::cout << "mat2.numberOfElements(): " << mat2.numberOfElements() << '\n';

std::cout << '\n';

}

(1) deklariert das primĂ€re Template, (2) definiert die partielle Spezialisierung und (3) die vollstĂ€ndige Spezialisierung von Matrix. Die Memberfunktionen numberOfElements werden außerhalb des Klassenkörpers definiert. Zeile (4) ist wohl die nicht-intuitive Zeile. Wenn die Memberfunktion numberOfElements außerhalb des Klassenkörpers definiert wird, darf kein template <> verwendet werden. Zeile (5) bewirkt die Instanziierung der partiellen und Zeile (6) die Instanziierung der vollstĂ€ndigen Spezialisierung.

Template Spezialisierung - Mehr Details zu Klassen-Templates

In meinem nĂ€chsten Beitrag schreibe ich ĂŒber die vollstĂ€ndige Spezialisierung von Funktions-Templates und deren ĂŒberraschendes Zusammenspiel mit Funktionen. Um es kurz zu machen, gemĂ€ĂŸ den C++ Core Guidelines gilt: T.144: Don't specialize function templates [5].


( [6])

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

Links in diesem Artikel:
[1] https://www.heise.de/blog/Template-Spezialisierung-6118187.html
[2] https://en.cppreference.com/w/cpp/types/is_same
[3] https://en.cppreference.com/w/cpp/types/remove_reference
[4] https://en.cppreference.com/w/cpp/types/integral_constant
[5] https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rt-specialize-function
[6] mailto:rainer@grimm-jaud.de