Menu
Coddy logo textTech

Variadische Templates

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 67 von 104.

Was ist, wenn Sie eine Funktion benötigen, die eine beliebige Anzahl von Argumenten beliebiger Typen akzeptiert? Herkömmliche Templates erfordern, dass Sie genau angeben, wie viele Typparameter Sie benötigen. Variadische Templates lösen dies, indem sie es Templates ermöglichen, eine beliebige Anzahl von Template-Argumenten zu akzeptieren.

Die Syntax verwendet ... (Ellipsis), um ein Parameter-Pack zu erstellen:

template <typename... Args>
void printAll(Args... args) {
    // Args ist ein Template-Parameter-Pack
    // args ist ein Funktions-Parameter-Pack
}

Um die Argumente zu verarbeiten, verwendet man normalerweise Rekursion mit einem Basisfall. Das Parameter-Paket wird Argument für Argument erweitert, bis keine mehr übrig sind:

// Basisfall: keine Argumente mehr übrig
void print() {
    std::cout << std::endl;
}

// Rekursiver Fall: erstes Argument verarbeiten, Rekursion mit dem Rest
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Verbleibende Argumente erweitern
}

print(1, 3.14, "hello", 'x');  // Ausgabe: 1 3.14 hello x

Sie können auch Fold-Ausdrücke (C++17) für einfachere Operationen ohne explizite Rekursion verwenden:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold-Ausdruck: addiert alle Argumente
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Ausgabe: 10

Variadische Templates bilden die Grundlage für viele Funktionen der Standardbibliothek wie std::make_unique, std::tuple und std::function. Sie ermöglichen typsichere Funktionen, die mit jeder Kombination von Argumenttypen und -anzahlen funktionieren.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein flexibles Logging-System erstellen, das variadische Templates verwendet, um Nachrichten mit einer beliebigen Anzahl von Argumenten zu verarbeiten. Sie werden Dienstprogramme erstellen, die Werte verketten, Argumente zählen und formatierte Ausgaben drucken können — alles unter Verwendung von Parameter-Packs.

Sie werden Ihren Code auf zwei Dateien verteilen:

  • Variadic.h: Definieren Sie hier Ihre variadischen Template-Funktionen.

    Erstellen Sie eine printAll-Funktion, die alle ihre Argumente durch Leerzeichen getrennt ausgibt, gefolgt von einem Zeilenumbruch. Verwenden Sie den rekursiven Ansatz mit einem Basisfall, der nur einen Zeilenumbruch ausgibt, und einem rekursiven Fall, der das erste Argument und ein Leerzeichen ausgibt und dann mit den restlichen Argumenten rekursiert.

    Erstellen Sie eine countArgs-Funktion, die die Anzahl der an sie übergebenen Argumente zurückgibt. Verwenden Sie sizeof..., um die Größe des Parameter-Packs zu ermitteln.

    Erstellen Sie eine sum-Funktion, die alle numerischen Argumente addiert und das Ergebnis zurückgibt. Verwenden Sie einen Fold-Ausdruck mit dem +-Operator.

    Erstellen Sie eine product-Funktion, die alle numerischen Argumente multipliziert und das Ergebnis zurückgibt. Verwenden Sie einen Fold-Ausdruck mit dem *-Operator.

  • main.cpp: Lesen Sie vier Eingaben ein (jede in einer separaten Zeile):
    1. Ein Integer
    2. Ein Double
    3. Ein String
    4. Ein Zeichen (Char)

    Demonstrieren Sie Ihre variadischen Templates durch:

    1. Aufrufen von printAll mit allen vier Eingaben und Ausgeben des Ergebnisses
    2. Ausgeben der Anzahl der Argumente beim Aufruf von countArgs mit dem Integer, Double und Zeichen: Argument count: <count>
    3. Berechnen der Summe aus dem Integer, dem Double und den Werten 10 und 5, dann Ausgeben von: Sum: <result>
    4. Berechnen des Produkts aus dem Integer und den Werten 2 und 3, dann Ausgeben von: Product: <result>
    5. Aufrufen von printAll mit nur dem String (einzelnes Argument)
    6. Aufrufen von printAll ohne Argumente (sollte nur einen Zeilenumbruch ausgeben)

Zum Beispiel bei den Eingaben 5, 2.5, Hello und X:

5 2.5 Hello X 
Argument count: 3
Sum: 22.5
Product: 30
Hello 

Beachten Sie, wie printAll eine beliebige Anzahl von Argumenten gemischter Typen verarbeitet, von vier Argumenten bis hin zu null. Die Funktionen sum und product verwenden Fold-Ausdrücke, um alle Werte elegant ohne explizite Rekursion zu kombinieren. Ihre countArgs-Funktion zeigt, wie sizeof... Ihnen die Pack-Größe zur Kompilierzeit liefert.

Spickzettel

Variadic Templates ermöglichen es Templates, eine beliebige Anzahl von Template-Argumenten zu akzeptieren, indem die ... (Ellipsen-) Syntax verwendet wird, um ein Parameter Pack zu erstellen:

template <typename... Args>
void printAll(Args... args) {
    // Args ist ein Template-Parameter-Pack
    // args ist ein Funktions-Parameter-Pack
}

Verarbeiten Sie Argumente mittels Rekursion mit einem Basisfall:

// Basisfall: keine Argumente mehr übrig
void print() {
    std::cout << std::endl;
}

// Rekursiver Fall: erstes Argument verarbeiten, Rekursion mit dem Rest
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Verbleibende Argumente expandieren
}

print(1, 3.14, "hello", 'x');  // Ausgabe: 1 3.14 hello x

Verwenden Sie sizeof..., um die Anzahl der Argumente in einem Parameter Pack zu erhalten:

template <typename... Args>
size_t countArgs(Args... args) {
    return sizeof...(args);
}

Verwenden Sie Fold Expressions (C++17) für einfachere Operationen ohne explizite Rekursion:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold Expression: addiert alle Argumente
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Ausgabe: 10

Fold Expressions funktionieren auch mit anderen Operatoren wie der Multiplikation:

template <typename... Args>
auto product(Args... args) {
    return (args * ...);  // Multipliert alle Argumente
}

Probier es selbst

#include <iostream>
#include <string>
#include "Variadic.h"

using namespace std;

int main() {
    // Eingaben lesen
    int intVal;
    double doubleVal;
    string strVal;
    char charVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> strVal;
    cin >> charVal;
    
    // TODO: printAll mit allen vier Eingaben aufrufen
    
    // TODO: Argumentanzahl mit countArgs mit intVal, doubleVal und charVal ausgeben
    // Format: "Argument count: <count>"
    
    // TODO: Summe von intVal, doubleVal, 10 und 5 berechnen und ausgeben
    // Format: "Sum: <result>"
    
    // TODO: Produkt von intVal, 2 und 3 berechnen und ausgeben
    // Format: "Product: <result>"
    
    // TODO: printAll nur mit dem String aufrufen
    
    // TODO: printAll ohne Argumente aufrufen
    
    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