Les patrons du c++ permettent de fabriquer des fonctions et des classes paramétrées par des types.
Pourquoi des patrons, car le c++ ne propose pas une vrai édition de lien dynamique car il est fortement typé, que la fabrication de méta-classes est bloquée par la nécessaire compilation du code. Les patrons sont une réponse mal commode à ces manques.
Le plus grave étant l'absence de protocoles (objectif C) ou d'interfaces (Java) qui permettent de faire un contrôle dynamique de la classe utilisée dans le code générique.
Les patrons sont une conséquence du typage
fort.
L'objectif des patrons est la réutilisation d'algorithmes, la réutilisation de concepts se fait par héritage.
Par exemple : la fonction minimum de deux entiers
int min (int a, int b)
{return a < b ? a : b ;}
est similaire au minimum de 2 doubles.
Double min (double a, double b)
{return a < b ? a : b ;}
La solution du c une macro
# define min (a,b) ((a)<(b)?(a):(b))
La solution c++ un patron de fonction " fonction template ".qui a le défaut et l'avantage d'être non typé
// Une fonction de template
# include <iostream.h>
template <class TYPE_COMPARABLE>
TYPE_COMPARABLE maximum (TYPE_COMPARABLE a,
TYPE_COMPARABLE b)
{
return (a > b) ? a : b ;
}
void main (void)
{
int x = 12, y = -7 ;
float real = 3.1415 ;
char ch = 'A' ;cout
;
}<< "Resultats:" << endl
<< maximum (x,y) << endl
<< maximum (-34,y) << endl
<< maximum (real, float (y))<< endl
<< maximum (real, float (x))<< endl
<< maximum (ch,'x') << endl
// Resultats :
// 12
// -7
// 3.141
// 12.000
// x
Création des trois fonctions (instanciations) à la compilation.
Dans l'exemple la liste est <class TYPE_COMPARABLE> il est possible d'avoir une liste plus longue template <class Automates,class Etat,class Transition>.
Dans l'exemple l'identificateur de type TYPE_COMPARABLE peut-être utilisé dans la portée du template qui est égale à celle de la fonction ou de la classe décrite par le template.
Chaque identificateur de type ne peut apparaître
qu'une fois dans la liste des paramètres formels.
// déclaration
template <class t_comparable>
t_comparable min (type*,int); // min d'un tableau
// instanciations
int ival ;
min (ival,3);//erreur n'est pas de type pointeur
const int icval = 10 ;
min (& icval,1); //erreur const int* et pas int*
// Deuxième versiontemplate <class T> T min (T, T) ;
template <class t_comparable>
t_comparable min (const t_comparable *, int) ;
min (& ival,1) ; //ok conversion triviale
min (& icval,1); //ok
min (& ival,o) ; //t_comparable º int
min (argv,argc); //t_comparable º char *
min (argc,argc); //erreur
// avec un cast
min (argv,(int)10 u); //ok
La solution consiste à surcharger le template c-a-d écrire explicitement la version pour les char*.
Char * min (char * a, char * b)Attention il faut aussi écrire :
{return (strcmp(a,b)< o ? a : b);}
const char* min(const char* a,const char* b)
{. . . }
erreur les deux templates ont la même signature c-a-d les deux fonctions ont des paramètres de même type.template <class T> int max (T, T) ;
et
template <class U> U max ( U, U) ;
Dans le cas suivant notre fonction vérifie une association :
template <class T> Asso (T,T);
maisd'où l'écriture :
Asso (unsigned)o,(int)o); //erreur
template <class T, class u> ? Asso (T,U);mais avec quelle valeur de retour comme la signature ne contient pas la valeur de retour nous ne pouvons pas écrire 2...
Une solution addoc un paramètre de typage :
template <class U, class T, class RT>
RT Asso (U,T,RT) ;
a. une seule
b. plusieurs
c. zérook ® terminé
erreur ® terminé
® (2)
a. une seule
b. plusieurs
c. zérook ® terminé
erreur ® terminé
® (3)
En effet, il faut que le compilateur ET le linker s'assure que l'on trouvera une et une seule copie de chaque instanciation nécessaire et AUCUNE instanciation inutile.
Deux approches :
La deuxième solution se conjugue de plusieurs
façons :
HPCC | un répertoire spécial ./ptrepository contient le code intermédiaire, ce code intermédiaire permet de générer les instances nécessaires à la compilation. |
cc | d'autres versions chez d'autres constructeurs. |
Avec HPCC on peut donc utiliser de la compilation
séparée de template ! (mais ce n'est pas portable ? ).
.Brutale - ne rien faire - les templates sont inclus partout ® ! temps de compilation
! taille d'exécutable
.L'option - f external - template
on utilise deux pragmas de compilation :# pragma interface
dans les fichiers contenant des définitions de template.
# pragma implémentation
dans le fichier dans lequel on veut que l'instanciation
soit réalisée (attention : un seul fichier).
.L'option - f no - implicit - template
il faut demander explicitement l'instanciation avec le type que vous voulez, avec la syntaxe suivante :
# include "Foo.h"
# include "Foo.C"
template class Foo <int> ;
instanciation explicite de même pour une fonction.
Il est possible d'avoir des paramètres expressions (ou le type est instancié).template <class T> class file ;
Dans le template le nom du template permet de parler de toutes ces instances :Template <class T, int> class tableau ;
Création de l'instance du patron et d'une instance de cette instance :
template <class Type> class MaillonFile { public :MaillonFile (const Type &) ; } ;
private :Type element ;
MaillonFile * suivant ;
//instance MaillonFile <int> MaillonFile <int> fe (103) ;
Chaîne s ("coucou") ;
MaillonFile <chaîne> fc (s) ;//avec fe comme instance. s, fe et fc sont des instances "classiques"
Z efficacité : Maillon File (const Type & x) : element (x) { }
Toutes les classes List sont amies de toutes les classes List Elément !template <class Type>
class list Element {...
template <class T> friend class List ;
} ;
Définition de << sur la class Listtemplate <class type>
Class List Element {...
friend class list <Type> ;
} ;
il faut que l'opérateur soit ami de la class list
template <class T>
ostream & operator << (ostream & as, list <T> & e){as << "< " ;
list Element <T> * p ;
for(p = e ® début ( ); p ; p = p ® suivant)
as << *p << ", " ;
as << ">" ;
return as ;
}//de légation de << sur
//list Element
template <class T> class list { ...Nous devons faire de même pour List Element, on écrira dans l'opérateur
friend ostream & operator << (ostream &,list <T> &);
...
};
as << p. element ;Si l'opérateur << n'est pas défini pour le type avec lequel vous avez instancié le template le compilateur vous signalera une erreur.
Z Mais attention
si vous n'utilisez pas << sur une liste définie pour ce type
le compilateur ne dira rien !
Ce qui est
un défaut comme une qualité !
typedef char * p char;il est aussi possible de spécifier la classe
List Element <p char> :: list Element (const p char & p)
{
element = new char [s tr l en (p) + 1];
assert (element);
s trcp y (element, p);
suivant = NULL ;
}
List <p char> en entier.
template <class T, int size>class tableau { ...
private :
T data [size];
...
} ;tableau <int,10> t ;//tableau de 10 int!
class list Element {...
static List Element <Type> * reserve ;
static const int taille-block ;
...
};
template <class T>
List Element <T> * list Element <T> :: reserve = NULL ;
template <class T>
const int list Element <T> :: taille-block = 24 ;
const int list Element <char *> :: taille-block = 1024 ;
template <class Z>
class X { Z val ;
friend foo ( ); //1
friend salad boeuf <Z> ; //2
friend saussice frite <Z>( ); //2
friend delta : : gamma (X <Z>); //2
template <class T> friend class toto ; //3
template <class T> friend void bak ( X <T>) ; //3
template <class T> friend void g <T> : : g ( ) ; //3
};