Templates de fonctions
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 64 sur 104.
Imaginez écrire une fonction pour trouver le maximum de deux entiers, puis réaliser que vous avez besoin de la même logique pour les nombres à virgule flottante, et encore une fois pour les chaînes de caractères. Sans les modèles, vous écririez des fonctions presque identiques pour chaque type. Les modèles de fonction résolvent ce problème en vous permettant d’écrire la logique une seule fois et de laisser le compilateur générer automatiquement des versions spécifiques à chaque type.
Un modèle de fonction utilise le mot-clé template suivi de paramètres de modèle entre chevrons :
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(5, 3) << std::endl; // Utilise la version int
std::cout << maximum(3.14, 2.71) << std::endl; // Utilise la version double
std::cout << maximum('a', 'z') << std::endl; // Utilise la version char
}Le compilateur examine chaque appel et génère une fonction concrète pour ce type spécifique. Ce processus est appelé instanciation de modèle. Vous pouvez également spécifier explicitement le type lorsque cela est nécessaire :
std::cout << maximum<double>(5, 3.14) << std::endl; // Force la version doubleLes modèles peuvent avoir plusieurs paramètres de type, ce qui permet des conceptions encore plus flexibles :
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*
printPair(3.14, 100); // T=double, U=intLes modèles de fonctions fournissent un polymorphisme à la compilation : le type est déterminé lors de la compilation du code, et non lors de l’exécution. Cela signifie qu’il n’y a aucune surcharge à l’exécution par rapport aux fonctions virtuelles, ce qui rend les modèles idéaux pour le code générique où les performances sont essentielles.
Défi
FacileConstruisons une boîte à outils utilitaire à l’aide de modèles de fonctions afin de créer des opérations réutilisables qui fonctionnent avec tout type compatible. Vous organiserez vos fonctions génériques dans un fichier d’en-tête et démontrerez leur flexibilité dans votre programme principal.
Vous allez créer deux fichiers :
MathUtils.h: définissez une collection de modèles de fonctions qui effectuent des opérations courantes :minimum: une fonction modèle qui prend deux valeurs du même type et renvoie la plus petite.clamp: une fonction modèle qui prend trois paramètres : une valeur, une limite inférieure et une limite supérieure. Elle renvoie la valeur limitée à l’intérieur de ces bornes (renvoie low si value est inférieure à low, high si value est supérieure à high, sinon renvoie la valeur elle-même).swapValues: une fonction modèle qui prend deux références du même type et échange leurs valeurs.main.cpp: lisez six entrées (chacune sur une ligne distincte) :- Premier entier
- Deuxième entier
- Une valeur double à limiter
- Limite inférieure (double)
- Limite supérieure (double)
- Un caractère
Démontrez vos modèles en :
- trouvant le minimum des deux entiers et en l’affichant :
Min of <a> and <b>: <result> - trouvant le minimum des caractères
'm'et de votre caractère d’entrée, puis en l’affichant :Min of m and <char>: <result> - limitant la valeur double et en l’affichant :
Clamp <value> to [<low>, <high>]: <result> - limitant le premier entier à l’intervalle [0, 100] et en l’affichant :
Clamp <value> to [0, 100]: <result> - échangeant les deux entiers et en l’affichant :
After swapValues: <a>, <b>
Par exemple, avec les entrées 25, 10, 3.7, 1.0, 5.0 et z :
Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25Remarquez comment chaque fonction modèle fonctionne sans difficulté avec les entiers, les nombres doubles et les caractères : le compilateur génère la version appropriée pour chaque type que vous utilisez. Votre fonction swapValues doit modifier les variables d’origine par l’intermédiaire de références, démontrant ainsi que les modèles fonctionnent avec des paramètres de référence comme les fonctions ordinaires.
Essayez vous-même
#include <iostream>
#include "MathUtils.h"
using namespace std;
int main() {
// Lire les entrées
int a, b;
double value, low, high;
char ch;
cin >> a;
cin >> b;
cin >> value;
cin >> low;
cin >> high;
cin >> ch;
// TODO: Utiliser le template minimum pour trouver le min de deux entiers
// Afficher : "Min of <a> and <b>: <result>"
// TODO: Utiliser le template minimum pour trouver le min de 'm' et du caractère d'entrée
// Afficher : "Min of m and <char>: <result>"
// TODO: Utiliser le template clamp sur la valeur double
// Afficher : "Clamp <value> to [<low>, <high>]: <result>"
// TODO: Utiliser le template clamp sur le premier entier avec l'intervalle [0, 100]
// Afficher : "Clamp <value> to [0, 100]: <result>"
// TODO: Utiliser le template swapValues sur les deux entiers
// Print: "After swapValues: <a>, <b>"
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