Templates variadiques
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 67 sur 104.
Que faire si vous avez besoin d’une fonction qui accepte un nombre quelconque d’arguments de n’importe quels types ? Les modèles traditionnels vous obligent à spécifier exactement le nombre de paramètres de type dont vous avez besoin. Les modèles variadiques résolvent ce problème en permettant aux modèles d’accepter un nombre arbitraire d’arguments de modèle.
La syntaxe utilise ... (points de suspension) pour créer un paquet de paramètres :
template <typename... Args>
void printAll(Args... args) {
// Args est un pack de paramètres de template
// args est un pack de paramètres de fonction
}Pour traiter les arguments, on utilise généralement la récursivité avec un cas de base. Le paquet de paramètres se développe un argument à la fois jusqu'à ce qu'il n'en reste aucun :
// Cas de base : plus d'arguments restants
void print() {
std::cout << std::endl;
}
// Cas récursif : traiter le premier argument, récursivité 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 xVous pouvez également utiliser les expressions de repli (C++17) pour des opérations plus simples, sans récursion explicite :
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expression de fold : ajoute tous les arguments
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Sortie : 10Les modèles variadiques sont à la base 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 sûres du point de vue des types, 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 modèles variadiques pour gérer des messages comportant un nombre quelconque d’arguments. Vous créerez des utilitaires capables de concaténer des valeurs, de compter les arguments et d’afficher un résultat formaté, le tout à l’aide de packs de paramètres.
Vous organiserez votre code sur deux fichiers :
Variadic.h: définissez ici vos fonctions de modèles variadiques.Créez une fonction
printAllqui affiche tous ses arguments séparés par des espaces, suivis d’un retour à la ligne. Utilisez l’approche récursive avec un cas de base qui affiche uniquement un retour à la ligne, et un cas récursif qui affiche le premier argument, un espace, puis effectue une récursion avec les arguments restants.Créez une fonction
countArgsqui renvoie le nombre d’arguments qui lui sont transmis. Utilisezsizeof...pour obtenir la taille du pack de paramètres.Créez une fonction
sumqui additionne tous les arguments numériques et renvoie le résultat. Utilisez une expression de réduction avec l’opérateur+.Créez une fonction
productqui multiplie tous les arguments numériques et renvoie le résultat. Utilisez une expression de réduction avec l’opérateur*.main.cpp: lisez quatre entrées (chacune sur une ligne distincte) :- Un entier
- Un nombre à virgule flottante de type double
- Une chaîne de caractères
- Un caractère
Démontrez vos modèles variadiques en :
- Appelant
printAllavec les quatre entrées et en affichant le résultat - Affichant le nombre d’arguments lors de l’appel de
countArgsavec l’entier, le nombre à virgule flottante et le caractère :Argument count: <count> - Calculant la somme de l’entier, du nombre à virgule flottante 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 uniquement la chaîne de caractères (un seul argument) - Appelant
printAllsans argument (doit afficher uniquement un retour à la 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 un nombre quelconque d’arguments de types mixtes, de quatre arguments à zéro. Les fonctions sum et product utilisent des expressions de réduction pour combiner élégamment toutes les valeurs sans récursion explicite. Votre fonction countArgs montre comment sizeof... vous donne la taille du pack au moment de la compilation.
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: Print argument count using countArgs with intVal, doubleVal, and 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 externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents 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 de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des 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 des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne