Menu
Coddy logo textTech

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: hello

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;
}

isEqual(5, 5);              // Verwendet die generische Version
isEqual("hi", "hi");        // Verwendet die spezialisierte Version

Der Compiler bevorzugt immer die spezifischste Übereinstimmung – wenn eine Spezialisierung für den exakt verwendeten Typ existiert, wird diese dem generischen Template vorgezogen.

challenge icon

Aufgabe

Einfach

Lassen 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 namens Formatter, 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: true oder Boolean: 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 als Pair: [<first>, <second>] ausgibt.

    Erstellen Sie eine Spezialisierung von formatPair für const char*, die jede Zeichenkette in Anführungszeichen setzt: Pair: ["<first>", "<second>"]

  • main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):
    1. Eine Ganzzahl (Integer)
    2. Ein Double
    3. Einen booleschen Wert als String ("true" oder "false")
    4. Einen String-Wert

    Demonstrieren Sie Ihre Formatierer, indem Sie entsprechende Formatter-Objekte erstellen und format() für jeden Typ aufrufen:

    1. Formatieren Sie die Ganzzahl mit Formatter<int>
    2. Formatieren Sie das Double mit Formatter<double>
    3. Formatieren Sie den booleschen Wert mit Formatter<bool> (konvertieren Sie die String-Eingabe zuerst in einen bool)
    4. Formatieren Sie den String mit Formatter<const char*>

    Demonstrieren Sie dann die Spezialisierung des Funktionstemplates:

    1. Rufen Sie formatPair mit zwei Ganzzahlen auf: 10 und 20
    2. Rufen Sie formatPair mit 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;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung