'>

les Modèles

Modèles de fonctions
Modèles de fonctions sont des fonctions spéciales qui peuvent fonctionner avec des types génériques. Cela nous permet de créer un modèle de fonction dont la fonctionnalité peut être adapté à plus d'un type ou une classe sans répéter l'ensemble du code pour chaque type.

En C + + cela peut être réalisé en utilisant les paramètres du modèle. Un paramètre de modèle est un type spécial de paramètre qui peut être utilisé pour passer un type comme argument: tout comme paramètres de la fonction réguliers peuvent être utilisés pour transmettre des valeurs à une fonction, paramètres du modèle permettent de passer outre les types de fonction. Ces modèles de fonction peuvent utiliser ces paramètres comme s'ils étaient tout autre type régulier.

Le format pour déclarer des modèles de fonction avec des paramètres de type est:

modèle function_declaration de <Identificateur classe>;
modèle function_declaration de <Identificateur <typename;

La seule différence entre les deux prototypes est l'utilisation soit la classe de clé ou le mot-clé typename. Son utilisation est indistinct, puisque les deux expressions ont exactement le même sens et se comportent exactement de la même façon.

Par exemple, pour créer un modèle de fonction qui renvoie le plus grand des deux objets que nous pourrions utiliser:

1
 2
 3
 4


   

 template < class myType> myType GetMax (myType a, myType b) { return (a>b?a:b); }



Ici, nous avons créé une fonction de modèle avec myType comme paramètre de modèle. Ce paramètre de modèle représente un type qui n'a pas encore été précisée, mais qui peut être utilisé dans la fonction de modèle comme s'il s'agissait d'un type ordinaire. Comme vous pouvez le voir, la fonction modèle getMax retourne le plus élevé des deux paramètres de ce type, non encore défini.

Pour utiliser ce modèle de fonction, nous utilisons le format suivant pour l'appel de fonction:

function_name <type> (paramètres);

Par exemple, pour appeler getMax de comparer deux valeurs entières de type int, nous pouvons écrire:

 1
 2


   

 int x,y; GetMax < int > (x,y);



Lorsque le compilateur rencontre cet appel à une fonction de modèle, il utilise le modèle pour générer automatiquement une fonction remplaçant chaque apparition de myType par le type passée en paramètre de modèle réel (int dans ce cas), puis l'appelle. Ce processus est effectué automatiquement par le compilateur et est invisible pour le programmeur.

Voici l'exemple complet:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20


   

 // function template #include <iostream> using namespace std; template < class T> T GetMax (T a, T b) { T result; result = (a>b)? a : b; return (result); } int main () { int i=5, j=6, k; long l=10, m=5, n; k=GetMax< int >(i,j); n=GetMax< long >(l,m); cout << k << endl; cout << n << endl; return 0; }

   

  6
  10



Dans ce cas, nous avons utilisé T comme nom de paramètre de modèle au lieu de myType parce qu'elle est plus courte et, en fait, est un nom très commun de paramètre de modèle. Mais vous pouvez utiliser n'importe quel identifiant vous aimez.

Dans l'exemple ci-dessus, nous avons utilisé le modèle de fonction getMax () deux fois. La première fois avec des arguments de type int et la seconde avec des arguments de type long. Le compilateur a instancié et a ensuite appelé à chaque fois que la version appropriée de la fonction.

Comme vous pouvez le voir, le type T est utilisé dans la fonction de modèle getMax (), même de déclarer de nouveaux objets de ce type:



   

 T result;



Par conséquent, le résultat sera un objet du même type que les paramètres a et b lorsque le modèle de fonction est instancié avec un type spécifique.

Dans ce cas précis où le type générique T est utilisée comme paramètre pour getMax le compilateur peut découvrir automatiquement le type de données doit instancier sans avoir à spécifier explicitement dans les crochets (comme nous l'avons fait avant de préciser <int> et <longue >). Donc, nous aurions pu écrire à la place:

 1
 2


   

 int i,j; GetMax (i,j);



Étant donné que i et j sont de type int, et le compilateur peut automatiquement découvrir que le paramètre de modèle ne peut être int. Cette méthode implicite produit exactement le même résultat:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18


   

 // function template II #include <iostream> using namespace std; template < class T> T GetMax (T a, T b) { return (a>b?a:b); } int main () { int i=5, j=6, k; long l=10, m=5, n; k=GetMax(i,j); n=GetMax(l,m); cout << k << endl; cout << n << endl; return 0; }

   

  6
  10



Remarquez comment dans ce cas, nous avons appelé notre modèle de fonction getMax () sans spécifier explicitement le type entre équerres <>. Le compilateur détermine automatiquement le type qui est nécessaire à chaque appel.

Parce que notre modèle de fonction ne comporte qu'un seul paramètre de modèle (classe T) et le modèle de la fonction elle-même accepte deux paramètres, à la fois de ce type T, nous ne pouvons pas appeler notre modèle de fonction avec deux objets de types différents comme arguments:

 1
 2
 3


   

 int i; long l; k = GetMax (i,l);



Ce ne serait pas correct, car notre modèle de fonction getMax attend deux arguments du même type, et dans cet appel à elle, nous utilisons des objets de deux types différents.

On peut aussi définir des modèles de fonction qui acceptent plus d'un paramètre de type, simplement en spécifiant plus de paramètres de modèles entre les chevrons. Par exemple:

 1
 2
 3
 4


   

 template < class T, class U> T GetMin (T a, U b) { return (a<b?a:b); }



Dans ce cas, notre modèle de fonction getMin () accepte deux paramètres de types différents et retourne un objet du même type que le premier paramètre (T) qui est passé. Par exemple, après cette déclaration que nous pourrions appeler getMin () avec:

 1
 2
 3


   

 int i,j; long l; i = GetMin< int , long > (j,l);



ou tout simplement:



   

 i = GetMin (j,l);



même si J et L sont de types différents, car le compilateur peut déterminer l'instanciation appropriée de toute façon.

Les modèles de Classe
Nous avons également la possibilité d'écrire des modèles de classe, de sorte qu'une classe peut avoir des membres qui utilisent des paramètres du modèle comme types. Par exemple:

 1
 2
 3
 4
 5
 6
 7
 8
 9


   

 template < class T> class mypair { T values [2]; public : mypair (T first, T second) { values[0]=first; values[1]=second; } };



La classe que nous venons de définir sert à stocker deux éléments de tout type valide. Par exemple, si nous voulions déclarer un objet de cette classe pour stocker deux valeurs entières de type int avec les valeurs 115 et 36 nous écrire:



   

 mypair< int > myobject (115, 36);



cette même classe également être utilisé pour créer un objet pour stocker n'importe quel autre type:



   

 mypair< double > myfloats (3.0, 2.18);



La seule fonction de membre dans le modèle de la classe précédente a été définie en ligne au sein de la déclaration de la classe elle-même. Dans le cas où nous définissons un membre de fonction en dehors de la déclaration du modèle de classe, nous devons toujours précéder cette définition avec le modèle <...> prefix:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26


   

 // class templates #include <iostream> using namespace std; template < class T> class mypair { T a, b; public : mypair (T first, T second) {a=first; b=second;} T getmax (); }; template < class T> T mypair<T>::getmax () { T retval; retval = a>b? a : b; return retval; } int main () { mypair < int > myobject (100, 75); cout << myobject.getmax(); return 0; }

   

  100



Notez la syntaxe de la définition de la fonction de membre getMax:

 1
 2


   

 template < class T> T mypair<T>::getmax ()



Confus par tant de T 's? Il ya de trois T dans cette déclaration: Le premier est le paramètre de modèle. Le deuxième T fait référence au type retourné par la fonction. Et le troisième T (celui entre crochets) est également une exigence: Il précise que le paramètre de modèle de cette fonction est également le paramètre de modèle de classe.

spécialisation de modèle
Si nous voulons définir une implémentation différente d'un modèle quand un type spécifique est passé comme paramètre de modèle, nous pouvons déclarer une spécialisation de ce modèle.

Par exemple, supposons que nous avons une classe très simple appelé MyContainer qui peut stocker un élément de tout type et qu'il a juste une fonction membre appelée augmentation, ce qui augmente sa valeur. Mais nous constatons que quand il stocke un élément de type char, il serait plus pratique d'avoir une application complètement différent avec un membre de fonction majuscule, si nous décidons de déclarer une spécialisation de modèle de classe pour ce type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34


   

 // template specialization #include <iostream> using namespace std; // class template: template < class T> class mycontainer { T element; public : mycontainer (T arg) {element=arg;} T increase () { return ++element;} }; // class template specialization: template <> class mycontainer < char > { char element; public : mycontainer ( char arg) {element=arg;} char uppercase () { if ((element>= 'a' )&&(element<= 'z' )) element+= 'A' - 'a' ; return element; } }; int main () { mycontainer< int > myint (7); mycontainer< char > mychar ( 'j' ); cout << myint.increase() << endl; cout << mychar.uppercase() << endl; return 0; }

   

  8
  J



Il s'agit de la syntaxe utilisée dans la spécialisation de modèle de classe:



   

 template <> class mycontainer < char > { ... };



Tout d'abord, remarquons que nous précéder le nom de modèle de classe avec un modèle vide <> de la liste des paramètres. Il s'agit de déclarer explicitement comme une spécialisation de modèle.

Mais plus important que ce préfixe, est le paramètre de spécialisation <char> après le nom du modèle de classe. Ce paramètre de spécialisation elle-même identifie le type pour lequel nous allons déclarer une spécialisation de classe de modèle (char). Remarquez les différences entre le modèle de classe générique et la spécialisation:

 1
 2


   

 template < class T> class mycontainer { ... }; template <> class mycontainer < char > { ... };



La première ligne représente le modèle générique, et le second est la spécialisation.

Lorsque nous déclarons spécialisations pour une classe de modèle, nous devons également définir tous ses membres, y compris ceux qui exactement égale à la classe de modèle générique, parce qu'il n'ya pas «héritage» de membres du modèle générique à la spécialisation.

Paramètres non-type pour les modèles
Outre les arguments de modèle qui sont précédés par la classe ou typename mots-clés, qui représentent les types, les modèles peuvent aussi avoir des paramètres typés réguliers, semblables à ceux trouvés dans les fonctions. Par exemple, jeter un oeil à ce modèle de classe qui est utilisé pour contenir des séquences d'éléments:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31


   

 // sequence template #include <iostream> using namespace std; template < class T, int N> class mysequence { T memblock [N]; public : void setmember ( int x, T value); T getmember ( int x); }; template < class T, int N> void mysequence<T,N>::setmember ( int x, T value) { memblock[x]=value; } template < class T, int N> T mysequence<T,N>::getmember ( int x) { return memblock[x]; } int main () { mysequence < int ,5> myints; mysequence < double ,5> myfloats; myints.setmember (0,100); myfloats.setmember (3,3.1416); cout << myints.getmember(0) << '\n' ; cout << myfloats.getmember(3) << '\n' ; return 0; }

   

  100
  3,1416



Il est également possible de définir des valeurs par défaut ou des types de paramètres du modèle de classe. Par exemple, si la définition du modèle de la classe précédente avait été:



   

 template < class T= char , int N=10> class mysequence {..};



Nous pourrions créer des objets en utilisant les paramètres par défaut du modèle en déclarant:



   

 mysequence<> myseq;



Ce qui serait équivalent à:



   

 mysequence< char ,10> myseq;



Modèles et projets multiples fichiers
Du point de vue du compilateur, les modèles ne sont pas des fonctions ou des classes normales. Ils sont établis sur la demande, ce qui signifie que le code d'une fonction de modèle n'est pas compilé jusqu'à une instanciation des arguments de modèle spécifique est nécessaire. A ce moment, quand une instanciation est nécessaire, le compilateur génère une fonction spécifiquement pour les arguments du modèle.

Lorsque les projets se développent, il est habituel de diviser le code d'un programme dans les différents fichiers de code source. Dans ces cas, l'interface et la mise en œuvre sont généralement séparés. Prendre une bibliothèque de fonctions comme, par exemple, l'interface se compose généralement de déclarations des prototypes de toutes les fonctions qui peuvent être appelées. Ceux-ci sont généralement déclarés dans un «fichier d'en-tête" avec une extension. H, et la mise en œuvre (la définition de ces fonctions) est dans un fichier indépendant avec code C + +.

Parce que les modèles sont compilées lorsque cela est nécessaire, ce qui force une restriction pour des projets multi-fichiers: la mise en oeuvre (définition) d'une classe ou fonction modèle doit être dans le même fichier que sa déclaration. Cela signifie que nous ne pouvons pas séparer l'interface dans un fichier d'en-tête séparée, et que nous devons comprendre à la fois l'interface et la mise en œuvre dans n'importe quel fichier qui utilise les modèles.

Comme aucun code est généré jusqu'à ce qu'un modèle est instancié lorsque cela est nécessaire, les compilateurs sont préparés pour permettre l'inclusion plus d'une fois le même fichier modèle avec les deux déclarations et de définitions dans un projet sans générer des erreurs de couplage. 
Publié par Drupal Study