Vue d'ensemble des modèles
Les modèles, parfois appelés types paramétrables, sont des mécanismes permettant de générer des fonctions et des classes basées sur des paramètres de type. Avec les modèles, vous pouvez concevoir une classe ou une fonction unique qui opère sur des données de nombreux types, plutôt que de créer une classe distincte pour chaque type.
Remarques
Par exemple, pour créer une fonction de type sécurisé qui retourne au moins deux paramètres sans utiliser de modèles, vous écrirez un ensemble de fonctions surchargées se présentant comme suit :
// what_are_templates1.cpp
// compile with: /c
// min for ints
int min( int a, int b ) {
return ( a < b ) ? a : b;
}
// min for longs
long min( long a, long b ) {
return ( a < b ) ? a : b;
}
// min for chars
char min( char a, char b ) {
return ( a < b ) ? a : b;
}
En utilisant des modèles, vous pouvez réduire cette duplication à un modèle de fonction unique :
// what_are_templates2.cpp
// compile with: /c
template <class T> T min( T a, T b ) {
return ( a < b ) ? a : b;
}
Les modèles permettent de diminuer considérablement la taille du code source et d'améliorer la flexibilité du code sans réduire la sécurité de type.
Il existe deux principaux types de modèles : les modèles de fonction et les modèles de classe. Dans l'exemple précédent, min est un modèle de fonction. Un modèle de classe est une classe avec un paramètre, par exemple :
// what_are_templates3.cpp
template <class T> class A {
T m_t;
public:
A(T t): m_t(t) {}
void f(T t);
};
int main() {
A<int> a(10);
}
Les modèles sont déclarés et définis de façon semblable à d'autres fonctions et classes, avec quelques différences majeures. Une déclaration de modèle ne définit pas complètement une fonction ou une classe ; elle définit uniquement la structure syntaxique de cette classe ou de cette fonction. La création d'une classe ou d'une fonction réelle s'effectue par le biais d'un processus appelé instanciation. Les différentes classes ou fonctions créées sont dites instanciées. Par exemple, un modèle de classe :
template <class T> struct A { . . . };
peut être utilisé pour instancier des classes pour A<int>, A<char>, A<int*>, A<MyClass*>, etc.
L'instanciation des classes ou des fonctions peut être effectuée explicitement ou implicitement. L'instanciation explicite est un moyen d'appeler dans le code les versions du modèle qui doivent être générées. L'instanciation implicite permet d'instancier les modèles en fonction des besoins au point auquel ils sont utilisés pour la première fois.
Les modèles peuvent également être paramétrés par un paramètre de valeur, auquel cas ce dernier est déclaré comme étant le paramètre d'une fonction. Vous ne pouvez utiliser ni les types à virgule flottante ni les types de classe comme paramètres de valeur.
// what_are_templates4.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
template <int i> class A {
int array[i];
public:
A() { memset(array, 0, i*sizeof(int)); }
};
int main() {
A<10> a;
}
Le problème qui se pose souvent avec les modèles est qu'ils peuvent être considérés comme une solution générique, ce qui signifie que le même code s'applique à tous les types. Si vous avez besoin de personnaliser le comportement du modèle pour un type particulier, vous pouvez utiliser la spécialisation. La spécialisation explicite permet de spécialiser un modèle pour un type réel particulier, et non pour un type générique. Un modèle de classe peut également être partiellement spécialisé, ce qui se révèle utile si vous possédez un modèle avec plusieurs paramètres de type et que vous souhaitez uniquement personnaliser le comportement par rapport à une partie des paramètres. Une spécialisation partielle est toujours générique et a besoin d'arguments template réels pour produire une classe instanciée réelle.