Templates variadiques
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 67 sur 104.
Et si vous aviez besoin d'une fonction qui accepte n'importe quel nombre d'arguments de n'importe quel type ? Les templates traditionnels vous obligent à spécifier exactement le nombre de paramètres de type dont vous avez besoin. Les templates variadiques résolvent ce problème en permettant aux templates d'accepter un nombre arbitraire d'arguments de template.
La syntaxe utilise ... (points de suspension) pour créer un paquet de paramètres (parameter pack) :
template <typename... Args>
void printAll(Args... args) {
// Args est un paquet de paramètres de modèle
// args est un paquet de paramètres de fonction
}Pour traiter les arguments, vous utilisez généralement la récursion avec un cas de base. Le pack de paramètres s'étend un argument à la fois jusqu'à ce qu'il n'en reste plus :
// Cas de base : aucun argument restant
void print() {
std::cout << std::endl;
}
// Cas récursif : traiter le premier argument, récurrence avec le reste
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // Étendre les arguments restants
}
print(1, 3.14, "hello", 'x'); // Sortie : 1 3.14 hello xVous pouvez également utiliser les expressions de repli (fold expressions) (C++17) pour des opérations plus simples sans récursion explicite :
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expression de repli : additionne tous les arguments
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Sortie : 10Les templates variadiques propulsent de nombreuses fonctionnalités de la bibliothèque standard telles que std::make_unique, std::tuple et std::function. Ils permettent de créer des fonctions à typage sûr qui fonctionnent avec n'importe quelle combinaison de types et de nombres d'arguments.
Défi
FacileConstruisons un système de journalisation flexible qui utilise des templates variadiques pour gérer des messages avec n'importe quel nombre d'arguments. Vous allez créer des utilitaires capables de concaténer des valeurs, de compter les arguments et d'afficher une sortie formatée — le tout en utilisant des packs de paramètres.
Vous organiserez votre code sur deux fichiers :
Variadic.h: Définissez vos fonctions de template variadique ici.Créez une fonction
printAllqui affiche tous ses arguments séparés par des espaces, suivis d'un saut de ligne. Utilisez l'approche récursive avec un cas de base qui affiche juste un saut de ligne, et un cas récursif qui affiche le premier argument, un espace, puis récurse avec les arguments restants.Créez une fonction
countArgsqui retourne le nombre d'arguments qui lui sont passés. Utilisezsizeof...pour obtenir la taille du pack de paramètres.Créez une fonction
sumqui additionne tous les arguments numériques et retourne le résultat. Utilisez une expression de repli (fold expression) avec l'opérateur+.Créez une fonction
productqui multiplie tous les arguments numériques et retourne le résultat. Utilisez une expression de repli (fold expression) avec l'opérateur*.main.cpp: Lisez quatre entrées (chacune sur une ligne séparée) :- Un entier
- Un double
- Une chaîne de caractères (string)
- Un caractère
Démontrez vos templates variadiques en :
- Appelant
printAllavec les quatre entrées et en affichant le résultat - Affichant le nombre d'arguments lors de l'appel à
countArgsavec l'entier, le double et le caractère :Argument count: <count> - Calculant la somme de l'entier, du double et des valeurs 10 et 5, puis en affichant :
Sum: <result> - Calculant le produit de l'entier et des valeurs 2 et 3, puis en affichant :
Product: <result> - Appelant
printAllavec seulement la chaîne de caractères (argument unique) - Appelant
printAllsans aucun argument (devrait afficher juste un saut de ligne)
Par exemple, avec les entrées 5, 2.5, Hello, et X :
5 2.5 Hello X
Argument count: 3
Sum: 22.5
Product: 30
Hello
Remarquez comment printAll gère n'importe quel nombre d'arguments de types mixtes, de quatre arguments jusqu'à zéro. Les fonctions sum et product utilisent des expressions de repli pour combiner élégamment toutes les valeurs sans récursion explicite. Votre fonction countArgs démontre comment sizeof... vous donne la taille du pack au moment de la compilation.
Aide-mémoire
Les templates variadiques permettent aux templates d'accepter un nombre arbitraire d'arguments de template en utilisant la syntaxe ... (points de suspension) pour créer un paquet de paramètres (parameter pack) :
template <typename... Args>
void printAll(Args... args) {
// Args est un paquet de paramètres de template
// args est un paquet de paramètres de fonction
}Traitez les arguments en utilisant la récursion avec un cas de base :
// Cas de base : plus d'arguments restants
void print() {
std::cout << std::endl;
}
// Cas récursif : traiter le premier argument, récurrence avec le reste
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // Développer les arguments restants
}
print(1, 3.14, "hello", 'x'); // Sortie : 1 3.14 hello xUtilisez sizeof... pour obtenir le nombre d'arguments dans un paquet de paramètres :
template <typename... Args>
size_t countArgs(Args... args) {
return sizeof...(args);
}Utilisez les expressions de repli (fold expressions) (C++17) pour des opérations plus simples sans récursion explicite :
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expression de repli : additionne tous les arguments
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Sortie : 10Les expressions de repli fonctionnent avec d'autres opérateurs comme la multiplication :
template <typename... Args>
auto product(Args... args) {
return (args * ...); // Multiplie tous les arguments
}Essayez vous-même
#include <iostream>
#include <string>
#include "Variadic.h"
using namespace std;
int main() {
// Lire les entrées
int intVal;
double doubleVal;
string strVal;
char charVal;
cin >> intVal;
cin >> doubleVal;
cin >> strVal;
cin >> charVal;
// TODO: Appeler printAll avec les quatre entrées
// TODO: Afficher le nombre d'arguments en utilisant countArgs avec intVal, doubleVal et charVal
// Format: "Argument count: <count>"
// TODO: Calculer et afficher la somme de intVal, doubleVal, 10 et 5
// Format: "Sum: <result>"
// TODO: Calculer et afficher le produit de intVal, 2 et 3
// Format: "Product: <result>"
// TODO: Appeler printAll avec seulement la chaîne
// TODO: Appeler printAll sans arguments
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique