Template-Spezialisierung
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 66 von 104.
Manchmal funktioniert ein generisches Template nicht gut für jeden Typ. Zum Beispiel vergleicht der Vergleich von C-Strings mit > Zeigeradressen und nicht den eigentlichen Text. Template-Spezialisierung ermöglicht es Ihnen, eine benutzerdefinierte Implementierung für bestimmte Typen bereitzustellen, während die generische Version für alles andere beibehalten wird.
Eine vollständige Spezialisierung ersetzt das gesamte Template für einen spezifischen Typ. Sie deklarieren sie mit einer leeren Template-Parameterliste und geben den konkreten Typ an:
// Primäres Template
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Vollständige Spezialisierung für const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // Ausgabe: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Ausgabe: String: helloFunktionstemplates können ebenfalls spezialisiert werden:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // Verwendet die generische Version
isEqual("hi", "hi"); // Verwendet die spezialisierte VersionDer Compiler bevorzugt immer die spezifischste Übereinstimmung – wenn eine Spezialisierung für den exakt verwendeten Typ existiert, wird diese dem generischen Template vorgezogen.
Aufgabe
EinfachLassen Sie uns ein typbewusstes Formatiersystem erstellen, das zeigt, wie die Template-Spezialisierung es Ihnen ermöglicht, das Verhalten für bestimmte Typen anzupassen, während ein generischer Fallback für alles andere beibehalten wird.
Sie werden zwei Dateien erstellen, um Ihren Template-Code zu organisieren:
Formatter.h: Definieren Sie ein Klassentemplate namensFormatter, das Werte für die Anzeige formatiert. Ihr generisches Template sollte mit jedem Typ funktionieren, aber Sie werden auch spezialisierte Versionen für Typen erstellen, die eine benutzerdefinierte Handhabung erfordern.Das primäre
Formatter-Template sollte Folgendes enthalten:- Eine
format()-Methode, die einen Wert vom Typ T entgegennimmt und Folgendes ausgibt:Value: <value>
Erstellen Sie eine vollständige Spezialisierung für
bool, die menschenlesbaren Text anstelle von 1 oder 0 ausgibt:- Die
format()-Methode sollte Folgendes ausgeben:Boolean: trueoderBoolean: false
Erstellen Sie eine vollständige Spezialisierung für
const char*, die Anführungszeichen um Zeichenketten setzt:- Die
format()-Methode sollte Folgendes ausgeben:String: "<value>"
Erstellen Sie außerdem ein Funktionstemplate namens
formatPair, das zwei Werte desselben Typs entgegennimmt und sie zusammen alsPair: [<first>, <second>]ausgibt.Erstellen Sie eine Spezialisierung von
formatPairfürconst char*, die jede Zeichenkette in Anführungszeichen setzt:Pair: ["<first>", "<second>"]- Eine
main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):- Eine Ganzzahl (Integer)
- Ein Double
- Einen booleschen Wert als String (
"true"oder"false") - Einen String-Wert
Demonstrieren Sie Ihre Formatierer, indem Sie entsprechende
Formatter-Objekte erstellen undformat()für jeden Typ aufrufen:- Formatieren Sie die Ganzzahl mit
Formatter<int> - Formatieren Sie das Double mit
Formatter<double> - Formatieren Sie den booleschen Wert mit
Formatter<bool>(konvertieren Sie die String-Eingabe zuerst in einen bool) - Formatieren Sie den String mit
Formatter<const char*>
Demonstrieren Sie dann die Spezialisierung des Funktionstemplates:
- Rufen Sie
formatPairmit zwei Ganzzahlen auf: 10 und 20 - Rufen Sie
formatPairmit zwei C-Strings auf:"hello"und"world"
Zum Beispiel bei den Eingaben 42, 3.14, "true" und "Hello":
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Beachten Sie, wie das generische Template Ganzzahlen und Doubles identisch behandelt, während die spezialisierten Versionen für bool und const char* eine benutzerdefinierte Formatierung bieten. Der Compiler wählt automatisch die spezifischste Übereinstimmung für jeden Typ aus, den Sie verwenden.
Spickzettel
Template-Spezialisierung ermöglicht es Ihnen, benutzerdefinierte Implementierungen für bestimmte Typen bereitzustellen, während eine generische Version für alle anderen Typen beibehalten wird.
Syntax der vollständigen Spezialisierung
Deklarieren Sie eine Spezialisierung mit einer leeren Template-Parameterliste template <> und geben Sie den konkreten Typ an:
// Primäres Template
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Vollständige Spezialisierung für const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Funktionstemplate-Spezialisierung
Funktionstemplates können ebenfalls spezialisiert werden:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
Compiler-Auswahl
Der Compiler bevorzugt immer die spezifischste Übereinstimmung – wenn eine Spezialisierung für den exakt verwendeten Typ existiert, wird diese gegenüber dem generischen Template bevorzugt.
Probier es selbst
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Eingaben lesen
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// String in bool konvertieren
bool boolVal = (boolStr == "true");
// TODO: Formatter<int> erstellen und den Integer formatieren
// TODO: Formatter<double> erstellen und den Double formatieren
// TODO: Formatter<bool> erstellen und den Boolean formatieren
// TODO: Formatter<const char*> erstellen und den String formatieren
// Hinweis: strVal.c_str() verwenden, um const char* zu erhalten
// TODO: formatPair mit zwei Integern aufrufen: 10 und 20
// TODO: formatPair mit zwei C-Strings aufrufen: "hello" und "world"
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container