Introduction : Qu'est-ce que les Generics (templates) en C++ et pourquoi en avons-nous besoin ?
Dans le monde de la programmation en C++, l'un des concepts clés permettant d'écrire du code flexible, réutilisable et type-safe est celui des templates. On les appelle souvent "generics" par analogie avec d'autres langages comme Java ou C#, bien qu'en C++ ce concept soit considérablement plus puissant et plus profond.
Imaginez que vous deviez écrire une fonction pour trouver l'élément maximum dans un tableau. Pour les entiers (int), ce serait une fonction ; pour les nombres à virgule flottante (double), une autre ; pour les chaînes de caractères (std::string), une troisième. Sans les templates, vous devriez dupliquer le code, ce qui entraîne des erreurs et complique la maintenance.
Les Generics (templates) résolvent ce problème. Ils permettent d'écrire du code qui fonctionne avec n'importe quel type de données, sans sacrifier les performances (contrairement aux fonctions virtuelles ou à void*). Les templates sont le fondement de la bibliothèque standard C++ (STL) : les conteneurs (std::vector, std::map), les algorithmes (std::sort, std::find) et les pointeurs intelligents (std::shared_ptr) sont tous implémentés à l'aide de templates.
Dans cet article, nous allons voir ce que sont les templates en C++, comment les déclarer et les utiliser correctement, et nous examinerons des exemples pratiques.
Partie principale : Comment utiliser les Generics (templates) en C++
1. Templates de fonctions (Function Templates)
La façon la plus simple de commencer avec les generics est de créer un template de fonction. Au lieu d'écrire plusieurs versions surchargées, vous en écrivez une seule, paramétrée par un type.
Syntaxe :
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}Le mot-clé template indique au compilateur qu'il s'agit d'un template. <typename T> déclare le paramètre de type T. Vous pouvez utiliser class à la place de typename — il n'y a pas de différence.
Exemple d'utilisation :
#include <iostream>#include <string>
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}
int main() { std::cout << max(10, 20) << std::endl; // int: 20 std::cout << max(3.14, 2.71) << std::endl; // double: 3.14 std::cout << max(std::string("C++"), std::string("Python")) << std::endl; // string: Python return 0;}Important : Le compilateur génère une version distincte de la fonction pour chaque type utilisé. C'est ce qu'on appelle l'instanciation du template. Cela permet d'atteindre des performances maximales — il n'y a pas de surcharge due aux appels virtuels.
Plusieurs paramètres de type :
template <typename T1, typename T2>void printPair(T1 a, T2 b) { std::cout << "(" << a << ", " << b << ")" << std::endl;}// printPair(1, "hello"); // Affichera : (1, hello)2. Templates de classes (Class Templates)
Les templates de classes permettent de créer des structures de données génériques. L'exemple classique est std::vector<T>. Vous pouvez créer votre propre classe template, par exemple une pile simple :
template <typename T>class Stack {private: T* data; size_t size; size_t capacity;public: Stack(size_t cap = 10) : size(0), capacity(cap) { data = new T[capacity]; } ~Stack() { delete[] data; }
void push(const T& value) { if (size < capacity) { data[size++] = value; } }
T pop() { if (size > 0) { return data[--size]; } throw std::out_of_range("Stack is empty"); }
bool isEmpty() const { return
```