Aller au menu - Aller au contenu

Icône Créer des templates

Mise à jour : 27/05/2011
Difficulté : Difficile Difficile Durée d'étude : 1 jour Creative Commons BY-NC-SA
75 639 visites depuis 7 jours, dont 490 sur ce chapitre classé 5/786
Revenons un peu en arrière et réfléchissons aux raisons pour lesquelles on cherche à programmer. Le but de la programmation, en tout cas à l'origine, est de simplifier les tâches répétitives en les faisant s'exécuter sur votre ordinateur plutôt que devoir faire tous les calculs à la main. On veut donc s'éviter du travail à la chaîne. Et cela reste valide pour le programmeur : s'il peut réutiliser un bout de code plutôt que de devoir le récrire, alors il gagne du temps. ;)

Dans les chapitres sur le polymorphisme, nous avons vu un moyen d'exécuter un code différent pour deux types semblables. Cette fois, nous allons voir comment faire s'exécuter un même code pour différents types de variables ou classes. Cela nous permettra d'éviter la tâche répétitive de réécriture de portions de code semblables pour différents types. Pensez à la classe vector. Quel que soit le type d'objet que l'on stocke, le tableau aura le même comportement : ajouter et supprimer des éléments, renvoyer sa taille, etc. Finalement, peu importe que ce soit un tableau d'entiers ou de nombres réels.

La force des templates est d'autoriser une fonction ou une classe à utiliser des types différents. C'est un mécanisme unique au C++ que l'on ne trouve que dans peu d'autres langages. La marque de fabrique des templates sont les chevrons < et > et vous l'aurez remarqué, la STL utilise énormément ce concept.

L'utilisation des templates est un sujet très vaste. Il y a même des livres entiers qui y sont consacrés tellement cela peut devenir complexe mais néanmoins puissant. Ce chapitre n'est qu'une brève introduction au domaine.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Les fonctions templates

Ce que l'on aimerait faire



Il arrive souvent qu'on ait besoin d'opérations mathématiques dans nos programmes. Une opération toute simple est celle qui consiste à trouver le plus grand de deux nombres. Dans le cas des nombres entiers, on pourrait écrire une fonction comme suit :

Code : C++ - La fonction maximum
1
2
3
4
5
6
7
int maximum(int a,int b)
{
   if(a>b)
      	return a;
   else
	return b;
}


Une telle fonction existe bien sûr dans la SL. Elle se trouve dans l'en-tête algorithm et s'appelle simplement max()


Cette fonction est très bien et elle n'a pas de problème. Cependant, si un utilisateur de votre fonction aimerait utiliser des double à la place des int, il risque d'avoir un problème. Il faudrait donc fournir également une version de cette fonction utilisant des nombres réels. Ce qui ne devrait pas vous poser de problème à ce stade du cours. ;)

Pour être rigoureux, il faudrait également fournir une fonction de ce type pour les char, les unsigned int les nombres rationnels, etc. On se rend vite compte que la tâche est très répétitive.
Cependant, il y a un point commun à toutes ces fonctions, le corps de la fonction est strictement identique. Quel que soit le type, le traitement que l'on effectue est le même. On se rend compte que l'algorithme utilisé dans la fonction est générique.

Il serait donc intéressant de pouvoir écrire une seule fois la fonction en disant au compilateur : « Cette fonction est la même pour tous les types, fais le sale boulot de recopie du code toi-même. » Eh bien, ça tombe bien parce que c'est ce que permettent les templates en C++ et c'est ce que nous allons apprendre à utiliser dans la suite.

Le terme français pour template est modèle. Le nom est bien choisi car il décrit précisément ce que nous allons faire. Nous allons écrire un modèle de fonction et le compilateur va utiliser ce modèle dans les différents cas qui nous intéressent.


Une première fonction template



Pour indiquer au compilateur que l'on veut faire une fonction générique, on va déclarer un « type variable » qui peut représenter n'importe quel autre type. On parle de type générique. Cela se fait de la manière suivante :

Code : C++ - Déclaration d'un type générique
1
template<typename T>


Vous pouvez remarquer quatre choses importantes.
  1. Premièrement le mot-clé template qui prévient le compilateur que la prochaine chose dont on va lui parler sera générique.
  2. Deuxièmement, les symboles "<" et ">" que vous avez certainement déjà aperçus dans le chapitre sur les vector et sur la SL. C'est la marque de fabrique des templates.
  3. Troisièmement, le mot-clé typename qui indique au compilateur que T sera le nom que l'on va utiliser pour notre « type spécial » qui remplace n'importe quoi.
  4. Finalement, il n'y a PAS de point-virgule à la fin de la ligne.


On peut également utiliser le mot-clé class à la place de typename dans ce contexte. Il n'y a aucune différence. Cela donne : template<class T>. J'utiliserai typename dans la suite pour éviter les confusions.


Secret (cliquez pour afficher)
Beaucoup de programmeurs utilisent class à la place de typename en invoquant la raison que cela fait 3 caractères de moins à taper... :-°


La ligne de code précédente indique au compilateur que dans la suite, T sera un type générique pouvant représenter n'importe quel autre type. On pourra donc utiliser ce T dans notre fonction comme type pour les arguments et pour le type de retour.

Code : C++ - Ma première fonction template
1
2
3
4
5
6
7
8
template <typename T>
T maximum(const T& a, const T& b)
{
   if(a>b)
      	return a;
   else
	return b;
}


Quand il va voir cela, le compilateur va automatiquement générer une série de fonctions maximum() pour tous les types dont vous avez besoin. Cela veut dire que si vous avez besoin de cette fonction pour des entiers, le compilateur va créer la fonction :

Code : C++
1
2
3
4
5
6
7
int maximum(const int& a,const int& b)
{
   if(a>b)
      	return a;
   else
	return b;
}


... et de même pour les double, char, etc. C'est le compilateur qui se farcit le travail de recopie ! Parfait, on peut aller faire la sieste pendant ce temps. :-°

On peut écrire un petit programme de test :

Code : C++ - Test de la fonction maximum
 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
#include <iostream>
using namespace std;

template <typename T>
T maximum(const T& a,const T& b)
{
   if(a>b)
      	return a;
   else
	return b;
}

int main()
{
     double pi(3.14);
     double e(2.71);

     cout << maximum<double>(pi,e) << endl;  // Utilise la "version double" de la fonction.

     int cave(-1);
     int dernierEtage(12);

     cout << maximum<int>(cave,dernierEtage) << endl;   // Utilise la "version int" de la fonction.

     unsigned int a(43);
     unsigned int b(87);

     cout << maximum<unsigned int>(a,b) << endl;   // Utilise la "version unsigned int" de la fonction.

     return 0;
}


Et tout cela se passe sans que l'on ait besoin d'écrire plus de code. Il faut juste indiquer entre des chevrons quelle "version" de la fonction on souhaite utiliser, comme pour les vector en somme : on devait indiquer quelle "version" du tableau on souhaitait utiliser.

Il n'est pas toujours utile d'indiquer entre chevrons quel type l'on souhaite utiliser pour les fonctions templates. Le compilateur est assez intelligent pour deviner ce que vous souhaitez faire. Mais dans des cas compliqués ou si il y a plusieurs arguments de types différents, alors il devient nécessaire de spécifier la version.

Code : C++
1
2
3
4
5
6
7
8
int main()
{
    double pi(3.14);
    double e(2.71);

    cout << maximum(pi,e) << endl;  // Utilise la "version double" de la fonction.
    return 0;
}


Le compilateur voit dans ce cas que l'on souhaite utiliser la "version double" de la fonction.
A vous de voir si votre compilateur comprend vos intentions. :p

Si vous êtes attentifs, vous avez peut-être remarqué que j'ai remplacé le passage par valeur pour les arguments par des références constantes. En effet, on ne sait pas quel type l'utilisateur va utiliser avec notre fonction maximum(). La taille en mémoire de ce type sera peut-être très grande ; on passe donc une référence constante pour éviter une copie coûteuse inutile.

Où mettre la fonction ?



Habituellement, un programme est subdivisé en plusieurs fichiers que l'on classe en deux catégories. Les fichiers de code (les .cpp) et les fichiers d'en-tête (les .h). Généralement, on met le prototype de la fonction dans un .h et la définition dans le .cpp comme on l'a vu tout au début ce ce cours.
Pour les fonctions templates, c'est différent. TOUT doit obligatoirement se trouver dans le fichier .h, sinon votre programme ne pourra pas compiler.

Je le répète encore une fois, car c'est une erreur classique, le prototype ET la définition d'une fonction template doivent obligatoirement se trouver dans un fichier d'en-tête.

Tous les types sont-ils utilisables ?



J'ai dit plus haut que le compilateur allait générer toutes les fonctions nécessaires. Cependant, il y a quand même une contrainte ici : le type que l'on passe à la fonction doit posséder un operator>. Par exemple, on ne peut pas utiliser cette fonction avec un Personnage ou un Magicien des chapitres précédents : ils ne possèdent pas de surcharge de >. Tant mieux, puisque prendre le maximum de deux Personnages n'a pas de sens !
Les contraintes dépendent des fonctions que vous écrivez. Si vous utilisez l'opérateur + dans la fonction, alors il faut que l'objet passé en argument surcharge cet opérateur. Si vous effectuez une copie dans la fonction, alors l'objet doit posséder un constructeur de copie etc.

Des fonctions plus compliquées

Vous aviez appris à écrire une fonction qui calcule la moyenne d'un tableau. A nouveau, les opérations à effectuer sont les mêmes quel que soit le type contenu. Écrivons donc cette fonction sous forme de modèle template.

Voici ma version :

Code : C++ - Moyenne. Un nouvel espoir
1
2
3
4
5
6
7
8
9
template<typename T>
T moyenne(T tableau[], int taille)
{ 
   T somme(0);                   //La somme des éléments du tableau
   for(int i(0); i<taille; ++i)
      somme += tableau[i];

   return somme/taille;
}


Tous les arguments d'une fonction ne doivent pas forcément être templates. Ici, taille est un entier tout ce qu'il y a de plus normal dans toutes les versions de la fonction.


Le problème que nous avions était que pour le type int, on se retrouvait avec une division entière qui posait problème (Les moyennes étaient arrondies vers le bas). Ce problème serait résolu si l'on pouvait utiliser un type différent de int pour la somme et donc la moyenne.

Pas de problème. Ajoutons donc un deuxième paramètre template pour le type de retour et utilisons le.

Code : C++ - Moyenne 2. Le template contre-attaque
1
2
3
4
5
6
7
8
9
template<typename T, typename S>
S moyenne(T tableau[], int taille)
{ 
   S somme(0);                //La somme des éléments du tableau
   for(int i(0); i<taille; ++i)
      somme += tableau[i];

   return somme/taille;
}


Avec cela, il est enfin possible de calculer la moyenne correctement. :soleil:
Par contre, il faut explicitement indiquer les types à utiliser lors de l'appel de la fonction. Le compilateur ne peut pas deviner quel type vous aimeriez pour S. :

Code : C++ - Moyenne 3. Le retour du main
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;

template<typename T, typename S>
S moyenne(T tableau[], int taille)
{
  S somme(0);                  //La somme des éléments du tableau                              
  for(int i(0); i<taille; ++i)
    somme += tableau[i];

  return somme/taille;
}

int main()
{
  int tab[5];
  //Remplissage du tableau

  cout << "Moyenne : " << moyenne<int,double>(tab,5) << endl;

  return 0;
}


De cette manière, on peut spécifier le type utilisé pour le calcul de la moyenne tout en préservant la liberté totale sur le type contenu dans le tableau. Pour bien assimiler le tout, je ne peux que vous inviter à faire quelques exercices, par exemple :

  • Écrire une fonction renvoyant le plus petit de deux éléments.
  • Réécrire la fonction moyenne() pour qu'elle reçoive en argument un std::vector<T> à la place d'un tableau statique.
  • Écrire une fonction template renvoyant un nombre aléatoire d'un type donné.

La spécialisation

Pour l'instant, nous n'avons essayé la fonction maximum() qu'avec des types de base. Essayons-la donc avec une chaîne de caractères :

Code : C++
1
2
3
4
5
6
int main()
{
  cout << "Le plus grand est: " << maximum<std::string>("elephant","souris") << endl;

  return 0;
}


Le résultat de ce petit programme est :
Code : Console
Le plus grand est: souris


On l'a déjà vu, l'opérateur < pour les chaînes de caractère compare selon l'ordre lexicographique. Mais imaginons (comme précédemment :p ) que le critère de comparaison qui nous intéresse est la longueur de la chaîne. Cela se fait en spécialisant la fonction template.

La spécialisation



La spécialisation se fait en utilisant la syntaxe suivante :
Code : C++
1
2
3
4
5
6
7
8
template <>
string maximum<string>(const string& a, const string& b)
{
  if(a.size()>b.size())
    return a;
  else
    return b;
}


Vous remarquerez deux choses:
  • La première ligne qui ne comporte aucun type entre < et >.
  • Le prototype de la fonction qui utilise cette fois le type que l'on veut et plus le type générique T.

Avec cette spécialisation, on obtient le comportement voulu :

Code : C++
1
2
3
4
5
6
int main()
{
  cout << "Le plus grand est: " << maximum<std::string>("elephant","souris") << endl;

  return 0;
}


qui donne :

Code : Console
Le plus grand est: elephant


La seule difficulté de la spécialisation est la syntaxe qui commence par la ligne template<>. Si vous vous souvenez de ça, vous savez tout.

Vous pouvez évidemment spécialiser la fonction pour plusieurs types différents. Il vous faudra alors créer une spécialisation par type.


L'ordre des fonctions



Pour pouvoir compiler et avoir le comportement voulu, votre programme devra être organisé d'une manière spéciale. Il faut respecter un ordre particulier :
  1. La fonction générique
  2. Les fonctions spécialisées

L'ordre est essentiel.
Lors de la compilation, le compilateur cherche une fonction spécialisée. S'il n'en trouve pas, alors il utilise la fonction générique déclarée au-dessus.

Les classes templates

Voyons maintenant comment réaliser des classes template, c'est-à-dire des classes dont le type des arguments peut varier. Cela peut vous sembler effrayant, mais vous en avez déjà utilisé beaucoup. Pensez à vector ou deque par exemple.
Il est temps de savoir réaliser des modèles de classes utilisables avec différents types.

Je vous propose de travailler sur un exemple que l'on pourrait trouver dans une bibliothèque comme Qt. Lorsque l'on veut dessiner des choses à l'écran, on utilise quelques formes de base qui servent à décomposer les objets plus complexes. L'une de ces formes est le rectangle qui comme vous l'aurez certainement remarqué est la forme des fenêtres ou des boutons entre autres. :o

Quelles sont les propriétés d'un rectangle ?

Un rectangle a quatre côtés, une surface et un périmètre. Les deux derniers éléments peuvent être calculés si l'on connaît les quatre arêtes. Voilà pour les attributs.

Quelles sont les actions qu'un rectangle peut effectuer ?


Ici, il y a beaucoup de choix. Nous choisirons donc les actions suivantes : vérifier si un point est contenu dans le rectangle et déplacer le rectangle.

Nous pourrions donc modéliser notre classe de la sorte :

Image utilisateur


On considère ici un rectangle parallèle aux bords de l'écran ce qui permet de simplifier les positions en utilisant un seul et unique nombre par côté.


Le type des attributs



Maintenant que nous avons modélisé la classe, il est temps de réfléchir aux types des attributs, en l'occurrence la position des côtés.

Si l'on veut avoir une bonne précision, alors il nous faut utiliser des double ou des float. Si par contre on considère que de toute façon l'écran est composé de pixels, on peut se dire que l'utilisation d'int est largement suffisante.

Les deux options sont possibles et on peut très bien avoir besoin des deux approches dans un seul et même programme. Et c'est là que vous devriez tous me dire : "Mais alors, utilisons donc des templates !". Vous avez bien raison. Nous allons écrire une seule classe qui pourra être instanciée avec différents types par le compilateur.

Création de la classe



Je suis sûr que vous connaissez la syntaxe même si je ne vous l'ai pas encore donnée. :euh: Comme d'habitude, on déclare un type générique T. Puis on déclare notre classe.

Code : C++ - La syntaxe
1
2
3
4
template <typename T>
class Rectangle{
    //...
};


Notre type générique est reconnu par le compilateur à l'intérieur de la classe. Utilisons-le donc pour déclarer nos quatre attributs.

Code : C++ - Les attributs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
template <typename T>
class Rectangle{
 
   //...

private:
   
   //Les côtés du Rectangle
   T m_gauche;
   T m_droite;
   T m_haut;
   T m_bas;

};


Voilà. Jusque-là, ce n'était pas bien difficile. Il ne nous reste plus qu'à écrire les méthodes. :-°

Les méthodes



Les fonctions les plus simples à écrire sont certainement les accesseurs qui permettent de connaître la valeur des attributs. La hauteur d'un rectangle est évidemment la différence entre la position du haut et la position du bas. Comme vous vous en doutez, cette fonction est template puisque le type de retour de la fonction sera un T.

Une première méthode



Nous pouvons donc écrire la méthode suivante :

Code : C++ - Première fonction membre
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
template <typename T>
class Rectangle{
public:
    //...

   T hauteur() const
   {
      return m_haut-m_bas;
   }

private:
   
   //Les cotes du Rectangle
   T m_gauche;
   T m_droite;
   T m_haut;
   T m_bas;
};


Vous remarquerez qu'il n'y a pas besoin de redéclarer le type template T juste avant la fonction membre puisque celui que nous avons déclaré avant la classe reste valable pour tout ce qui se trouve à l'intérieur.

Et si je veux mettre le corps de ma fonction à l'extérieur de ma classe ?


Bonne question. On prend souvent l'habitude de séparer le prototype de la définition. Et cela peut se faire aussi ici. Pour faire cela, on mettra le prototype dans la classe et la définition à l'extérieur mais il faut ré-indiquer qu'on utilise un type variable T :

Code : C++ - Fonction membre à l'extérieur
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
template <typename T>
class Rectangle{
public:
    //...

   T hauteur() const; 
   
    //...
};
 
template<typename T>
T Rectangle<T>::hauteur() const
{
   return m_haut-m_bas;
}


Vous remarquerez aussi l'utilisation du type template dans le nom de la classe puisque cette fonction sera instanciée de manière différente pour chaque T.

Souvenez-vous que tout doit se trouver dans le fichier .h !


Une fonction un peu plus complexe



Une des fonctions que nous voulions écrire est celle permettant de vérifier si un point est contenu dans le rectangle ou pas. Pour cela, on doit passer un point (x;y) en argument à la fonction. Le type de ces arguments doit évidemment être T, de sorte que l'on puisse comparer les coordonnées sans avoir de conversions.

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
template <typename T>
class Rectangle{
public:
 
   //...

   bool estContenu(T x, T y) const
   {
      return (x >= m_gauche) && (x <= m_droite) && (y >= m_bas) && (y <= m_haut);
   }

private:
    //...
};


Vous remarquerez à nouveau l'absence de redéfinition du type T. Quoi, je me répète ? ;) C'est sûrement que cela devient clair pour vous.

Constructeur



Il ne nous reste plus qu'à traiter le cas du constructeur. A nouveau, rien de bien compliqué, on utilise simplement le type T défini avant la classe.

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template <typename T>
class Rectangle{
public:

   Rectangle(T gauche, T droite, T haut, T bas)
        :m_gauche(gauche),
         m_droite(droite),
         m_haut(haut),
         m_bas(bas)
   {}

   //...
};


Et comme pour toutes les autres méthodes, on peut définir le constructeur à l'extérieur de la classe. Vous êtes bientôt des pros, je vous laisse donc essayer seuls.

On pourrait ajouter une fonction appelée dans le constructeur qui vérifie que le haut se trouve bien au-dessus du bas et de même pour droite et gauche.


Finalement, voyons comment utiliser cette classe.

Instanciation d'une classe template



Il fallait bien y arriver un jour ! Comment crée-t-on un objet d'une classe template et en particulier de notre classe Rectangle ?

En fait, je suis sûr que vous le savez déjà. :euh: Cela fait longtemps que vous créez des objets à partir de la classe template vector ou map. Si l'on veut un Rectangle composé de double, on devra écrire :

Code : C++
1
2
3
4
5
6
int main()
{
   Rectangle<double> monRectangle(1.0, 4.5, 3.1, 5.2);
  
   return 0;
}



L'utilisation des fonctions se fait ensuite comme d'habitude :

Code : C++
1
2
3
4
5
6
7
8
int main()
{
   Rectangle<double> monRectangle(1.0, 4.5, 3.1, 5.2);

   cout << monRectangle.hauteur() << endl;
  
   return 0;
}


Pour terminer ce chapitre, je vous propose d'ajouter quelques méthodes à cette classe. Je vous parlais d'une méthode deplacer() qui change la position du rectangle. Essayez autrement d'écrire les méthodes surface() et perimetre().

Finalement, pour bien tester tout ces concepts, vous pouvez refaire la classe ZFraction de sorte à ce que l'on puisse spécifier le type à utiliser pour stocker le numérateur et le dénominateur. Bonne chance !
C'est un des chapitres les plus avancés de ce cours... mais certainement un des plus intéressants. ;) Vous avez appris à faire travailler votre compilateur pour qu'il crée différentes versions d'une même fonction ou d'une même classe.

Ce chapitre n'est par contre qu'un bref aperçu. Il y aurait encore énormément de choses à dire sur le sujet, mais je suis convaincu que je vous ai donné envie d'en savoir plus dans le domaine.
Chapitre précédent Sommaire Chapitre suivant

Partager

6 commentaires pour "Créer des templates"
Note moyenne : 3.85 / 4 (1749 votes)
Pseudo Commentaire
En ligne Nanoc # Posté le 29/05/2011 à 00:38:01
Aimez-vous le C++ ?
Avatar
Validateurs

Ville : Durham
Pays : Royaume-Uni
Études : EPFL

Non non. Ce n'est pas correct. Il n'y a absolument pas besoin de les mettre dans le .cpp.
 
Hors ligne Copelnug # Posté le 29/05/2011 à 16:56:39

Je viens de tester avec G++. Et si le fichier des templates est inclus dans plusieurs fichiers cpp, le compilateur se plaint d’avoir des fonctions définies à plusieurs places.

Par exemple:
Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#ifndef TEMPLATEP_HPP_INCLUDED
#define TEMPLATEP_HPP_INCLUDED
#include <string>
#include <sstream>

template<typename T>
std::string to_string(const T& value)
{
	std::ostringstream stream;
	stream << value;
	return stream.str();
}

template<>
std::string to_string<std::string>(const std::string& value)
{
	return value;
}

#endif
provoque l’erreur si le code est inclus deux fois ou plus. Par contre, le déplacement de l’implémentation de la fonction spécialisée pour les string dans un fichier cpp règle le problème. Passer la fonction en inline fonctionnerait aussi.
Hors ligne god_tachmou # Posté le 07/06/2011 à 09:55:19
Robotique power !
Avatar

Avis : Très bon

Ville : Toulon
Pays : France métropolitaine
Études : SUPMECA Toulon

Cette phrase a été copiée deux fois d'affilé dans la partie "Les classes templates" :
Citation : Les classes templates
Voyons maintenant comment réaliser des classes template, c'est-à-dire des classes dont le type des arguments peut varier.


Excellent tutoriel.

N'hésitez pas à me contacter par MP si vous pensez que je peux vous aider.
 
Hors ligne germino # Posté le 23/06/2011 à 22:12:57
public programmer<C++>
Avatar

Avis : Très bon
Flux RSS

Ville : St germain laprade
Pays : France métropolitaine

Très bon. Mais vous oubliez de dire qu'on peut mettre plusieurs types template en une fonction...

Citation : Qui-Gon-Jin
Ce n'est pas parce que tu parles que tu es intelligent.
 
Hors ligne Programpriv # Posté le 13/04/2012 à 13:38:13
C'est beau C++ !
Avatar

Avis : Très bon

Cette introduction est vraiment intéressante et complète. Par contre, vous n'avez pas mis que nous n'étions pas obligés d'indiquer le type explicitement à la spécialisation. C'est un peu long à dire, un exemple sera plus parlant.

Code : C++
1
2
3
4
5
6
7
8
template <>
string maximum<string>(const string& a, const string& b)
{
  if(a.size()>b.size())
    return a;
  else
    return b;
}

Peut être écrit :

Code : C++
1
2
3
4
5
6
7
8
template <>
string maximum(const string& a, const string& b)
{
  if(a.size()>b.size())
    return a;
  else
    return b;
}

Étant donné que le type est déduit du compilateur. Sinon nickel.
 

Voir tous les commentaires