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 xSie 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: 10Variadische 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.
Aufgabe
EinfachLassen 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 Siesizeof..., 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):- Ein Integer
- Ein Double
- Ein String
- Ein Zeichen (Char)
Demonstrieren Sie Ihre variadischen Templates durch:
- Aufrufen von
printAllmit allen vier Eingaben und Ausgeben des Ergebnisses - Ausgeben der Anzahl der Argumente beim Aufruf von
countArgsmit dem Integer, Double und Zeichen:Argument count: <count> - Berechnen der Summe aus dem Integer, dem Double und den Werten 10 und 5, dann Ausgeben von:
Sum: <result> - Berechnen des Produkts aus dem Integer und den Werten 2 und 3, dann Ausgeben von:
Product: <result> - Aufrufen von
printAllmit nur dem String (einzelnes Argument) - Aufrufen von
printAllohne 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 xVerwenden 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: 10Fold 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;
}
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