Menu
Coddy logo textTech

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 x

Vous 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 : 10

Les 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.

challenge icon

Défi

Facile

Construisons 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 printAll qui 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 countArgs qui retourne le nombre d'arguments qui lui sont passés. Utilisez sizeof... pour obtenir la taille du pack de paramètres.

    Créez une fonction sum qui 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 product qui 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) :
    1. Un entier
    2. Un double
    3. Une chaîne de caractères (string)
    4. Un caractère

    Démontrez vos templates variadiques en :

    1. Appelant printAll avec les quatre entrées et en affichant le résultat
    2. Affichant le nombre d'arguments lors de l'appel à countArgs avec l'entier, le double et le caractère : Argument count: <count>
    3. Calculant la somme de l'entier, du double et des valeurs 10 et 5, puis en affichant : Sum: <result>
    4. Calculant le produit de l'entier et des valeurs 2 et 3, puis en affichant : Product: <result>
    5. Appelant printAll avec seulement la chaîne de caractères (argument unique)
    6. Appelant printAll sans 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 x

Utilisez 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 : 10

Les 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;
}
quiz iconTestez-vous

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