Vous êtes sur la page 1sur 13

Ecole Nationale de Techniques Avancées

Simulation Numérique
Chapitre 5
Programmation générique
Eric Lunéville
Programmation générique
Fonction Echange travaillant sur des des entiers, des réels,...
void Echange ( i nt &a , in t &b ) void Echange ( f l o a t &a , f l o a t &b )
{ i nt t=a ; { f l o a t t=a ;
a=b ; a=b ;
b=t ; b=t ;
} }
Autant de fonctions Echange que de types considérés !!!
Le C++ offre un mécanisme de généralisation : template
template <typename T> "Modèle" de fonction Echange
void Echange (T &a , T &b )
{T t=a ; avec un type muet typename T
a=b ;
b=t ; T substituable par tout type
} y compris des classes
Sim. Numérique – chapitre 5 - 1
Mécanisme de compilation
Concrètement, le compilateur remplace T par le type qui apparaît
lors de l'appel à la fonction Echange
int main ( )
{ i nt a =1; i nt b=2;
double x = 3 . 5 ; double y = 4 . ;
Echange ( a , b ) ; Echange avec "T=int"
Echange ( x , y ) ; Echange avec "T=double"
Echange ( a , x ) ; Erreur de compilation
} pas de version (int,double)
Au niveau du compilateur/lieur
Recherche la fonction void Echange(int,int)
Trouve template <typename T> void Echange(T &,T &)
Génère void Echange(int &,int &) en remplaçant T par int
Compile cette version : "instanciation" de la version int
certains compilateurs peuvent transtyper int en double
et instancier la version Echange(double&,double&) Sim. Numérique – chapitre 5 - 2
Programmation générique
Extension du concept aux classes
Programmation efficace, robuste (version unique de code)
Mise en oeuvre parfois difficile
"non inclus" dans le langage ("instanciation" de code)
Sensibilité aux compilateurs
Largement utilisé, en particulier par la STL
Plan
Programmation générique des fonctions
Programmation générique des classes
Spécialisation partielle ou totale
Mécanismes de compilation
Un exemple complet : la classe complex de la STL
Sim. Numérique – chapitre 5 - 3
Modèles de fonctions
Syntaxe générale d'une fonction modèle :
template <typename T1, typename T2, ...>
type_retour nom_fonction (type_arg1 , type_arg2 , ...)
Les types abstraits T1,T2,... doivent tous intervenir dans la
liste des arguments d'entrée
ils peuvent intervenir dans l'argument de sortie
le compilateur n'analyse que les arguments d'entrée !
template <typename T> template <typename T>
in t f o n c (T &i ) { return in t ( i ) ; } T f o n c ( i nt &i ) { return T( i ) ; }
in t main ( ) int main ( )
{ in t j=f o n c ( 1 . ) ; } {double x=f o n c ( 2 ) ; }
Erreur de compilation !
Sim. Numérique – chapitre 5 - 4
Modèles de fonctions
template <typename T1 , typename T2>
T1 f o n c ( T2 &t ) { return T1 ( t ) ; }
Même erreur de compilation !
template <typename T1 , typename T2>
T1 f o n c ( T1 &t1 , T2 &t 2 ) Ok, T1 et T2 dans les arguments
{...}
Syntaxes équivalentes :
template <typename T1> < typename T2>
type_retour nom_fonction (type_arg1 , type_arg2 , ...)
On peut utiliser class au lieu de typename :
template <class T1, class T2>
type_retour nom_fonction (type_arg1 , type_arg2 , ...)
Sim. Numérique – chapitre 5 - 5
Une fonction min générale
template<typename T>
T Min ( const T& a , const T& b ) { return ( ( a < b ) ? ( a ) : ( b ) ) ; }
template<typename T>
T Min ( const T& a , const T& b , const T& c ) { return Min ( Min ( a , b ) , c ) ; }
Fonctionne avec tout type et classe supportant l'opérateur <
On peut faire un Min sur des types différents :
template<typename T1 , typename T2>
T1 Min ( const T1& a , const T2& b ) { return ( ( a < b ) ? ( a ) : T1( b ) ) ; }
suppose un transtypage "consistant" du type T2 vers le type T1 !!!
détection à la compilation.
pas conseillé du tout fonction Min surprenante : Min(2.5,3)=2.5 et Min(3,2.5)=2
On ne peut pas avoir (ambiguïté) les deux versions de Min :
T1 Min(T1& a, T2& b) recouvre T Min(T& a, T& b)
Sim. Numérique – chapitre 5 - 6
Classe modèle
Généralisation du concept de modèle aux classes, syntaxe :
template <typename T1, typename T2, … >
class nom_classe {…};
Les types abstraits T1,T2,… doivent apparaître dans la définition de la classe
utilisés comme des types standards
template <typename T> c l a s s v e c t
{ public :
T∗ v a l ;
i nt dim ;
vect <T>(const in t d=0) {dim=d ; i f ( d>0) v a l=new T[ d ] ; }
T& operator ( ) ( const in t i )
{ i f ( i >0 && i<=dim ) return v a l [ i − 1 ] ;
e x i t ( −1) ; // e r r e u r }
};
Classe gérant des vecteurs de tout type; certains types seront incompatibles
avec les opérations prévues !
Sim. Numérique – chapitre 5 - 7
Classe modèle : instanciation
Instanciation d'un objet de la class vect en précisant le type abstrait T
in t main ( )
{ v ect <double> V( 2 ) ;
V( 1 ) = 1 . ; V( 2 ) = 2 . ;
v ect <char ∗> L ( 2 ) ;
L(1)= ” c h a i n e 1 ” ; L(2)= ” c h a i n e 2 ” ;
}
A la compilation :
recherche le type vect<double>
trouve template <typename T> class vect
génère le code explicite en remplaçant T par double
compile le code généré
recherche le constructeur vect<double>(int)
type composé : vect< vect<double> > pour un vecteur de vecteur (matrice)
Sim. Numérique – chapitre 5 - 8
Classe modèle : champ opératoire
Technique d'abstraction permettant de manipuler des objets complexes
en se focalisant sur les opérations de structure
Définition des opérations de structure ? Par exemple :
template <typename T> c l a s s v e c t
{ public :
...
v ect <T>& operator+=(const vect <T>& V)
{ f or ( i nt i =0; i <dim ; i ++) v a l [ i ]+=V. v a l [ i ] ;
return ∗ t h i s ; }
};
la classe vect ne pourra plus gérer des vecteurs de char * (+= non défini !)
Choix de conception : niveau d'abstraction souhaité ?
La classe vector de la STL n'implémente pas les opérations algébriques
(concept de liste à accès direct )
Sim. Numérique – chapitre 5 - 9
Classe modèle : membre modèle
Fonction membre générique dans une classe
par exemple : produit par un scalaire quelconque d'un vect
template <typename T> c l a s s v e c t
{ public :
...
template <typename S> vect <T>& operator∗=(const S& s )
{ f or ( int i =0; i <dim ; i ++) v a l [ i ]∗= s ;
return ∗ t h i s ; }
};
plus général que vect<T>& operator*=(const T& s)
le type abstrait S est spécifique à l'opérateur *=
peut poser des problèmes, cas classique :
vect <double> V( 2 ) ; bool c a s c o m p l e x e=f a l s e ;
i f ( c a s c o m p l e x e ) V∗=complex<double > ( 0 , 1 ) ;
else V∗=2;
erreur de compilation double*=complex<double> impossible
même si on ne passe pas dans le cas complexe !
Sim. Numérique – chapitre 5 - 10
Instanciation
Il y a deux modes d'instanciation d'un modèle :
implicite : faite par le compilateur s'il y arrive !!!
explicite : réalisée par l'utilisateur
- pour une fonction :
in t i=Min<int > ( 2 , 3 . ) ;
force l'instanciation de template <class T>Min(T&,T&) avec T=int
et force la conversion du réel 3. en entier 3
- pour une classe :
vec t <double> v e c t ;
force l'instanciation de la classe vect en double
i.e la compilation de la classe vect pour le type double
Sim. Numérique – chapitre 5 - 11
Spécialisation
On peut être amené à traiter des cas particuliers de modèles,
prévenir une mauvaise utilisation, traitement spécifique :
spécialisation de modèles
template <typename T> c l a s s v e c t
{ public :
...
template <typename S> vect <T>& operator∗=(const S& s )
{ f or ( i nt i =0; i <dim ; i ++) v a l [ i ]∗= s ;
return ∗ t h i s ; }
};
vec t <double>& v ect <double > : : operator∗=( const complex<double>& s )
{ // t r a i t e m e n t s p e c i f i q u e }
Redéfinition d'une instance particulière de l'opérateur *=
permet de résoudre le problème de compilation évoqué avant :
il existe une version de double*=complexe admissible
Sim. Numérique – chapitre 5 - 12
Template et implémentation séparée
Implémentation dissociée de la définition (syntaxe)
Pour les fonctions même syntaxe que la déclaration
Pour les fonctions membre d'une classe modèle :
template <typename T1, typename T2, ...>
arg_retour nom_classe<T1,T2,...>::nom_fonction(arg_entrée1,...)
Pour les fonctions membre modèle d'une classe modèle :
template <typename T1, typename T2, ...>
template <typename S1, typename S2, ...>
arg_retour nom_classe<T1,T2,...>::nom_fonction(arg_entrée1,...)
les modèles de la classe et ceux de la fonction sont dans 2 template distincts !
Sim. Numérique – chapitre 5 - 13
Template et implémentation séparée
vecteur.h vecteur.c++
#i f n d e f vectH #include ” v e c t e u r . h”
#define vectH template <typename T>
template <typename T> T& vect <T> : : operator ( ) ( const in t i )
class vect { i f ( i >0 && i<=dim )
{ public : return v a l [ i − 1 ] ; }
T∗ v a l ; template <typename T>
in t dim ; vec t <T>&
vect <T>(const in t d =0); vec t <T> : : operator+=(const T& V)
T& operator ( ) ( const in t i ) ; { f or ( i nt i =0; i <dim ; i ++)
vec t <T>& v a l [ i ]+=V. v a l [ i ] ;
operator+=(const T& V) ; return ∗ t h i s ; }
template <typename S> template <typename T>
vec t <T>& template <typename S>
operator∗=( const S& s ) ; vec t <T>&
}; vec t <T> : : operator∗=( const S& s )
#endif { f or ( i nt i =0; i <dim ; i ++)
v a l [ i ]∗= s ;
return ∗ t h i s ; }
Sim. Numérique – chapitre 5 - 14
Soucis de compilation ...
Principe de l’implémentation /compilation séparée :
main.c++ vecteur.h vecteur.c++
#include ” v e c t e u r . h” #i f n d e f vectH #include ” v e c t e u r . h”
int main ( ) #define v ec t H template <typename T>
{ vec t <double> V; template <typename T> . . .
. . . class vect { . . . };
} #endif
Compilation de vecteur.c++ et main.c++ (génère vecteur.o et main.o)
Edition de lien des objets vecteur.o et main.o (génère l’exécutable)
Ne fonctionne pas !!!
la compilation de vecteur.c++ ne produit rien (paramètre T non connu)
compilation de main.c++ :
trouve la définition vect<T> dans vecteur.h, remplace T par double
mais ne compile pas vecteur.c++ avec T=double (ne le connaît pas)
Comment faire ?
Sim. Numérique – chapitre 5 - 15
Soucis de compilation ...
Première méthode : tout mettre dans le fichier d’entête
main.c++ vecteur.h
#include ” v e c t e u r . h” #i f n d e f vectH
in t main ( ) #define vec t H
{ vec t <double> V; template <typename T>
. . . class vect { . . . };
} // i m p lé m e n t a t i o n
. . .
#endif
compilation de main.c++ :
trouve la définition de vect<double> dans vecteur.h (T=double)
compile l’implémentation de vect avec T=double qui s’y trouve !
Inconvénient : peut multiplier les versions compilés de vect<T>
pas très génant pour les petits codes
Sim. Numérique – chapitre 5 - 16
Soucis de compilation ...
Deuxième méthode : inclure le fichier d’implémentation
main.c++ vecteur.h vecteur.c++
#include ” v e c t e u r . h” #i f n d e f vectH template <typename T>
int main ( ) #define v ec t H . . .
{ vec t <double> V; template <typename T>
. . . class vect { . . . };
} // i m p lé m e n t a t i o n
#include ” v e c t e u r . c++”
#endif
Attention : ne pas inclure vecteur.h dans vecteur.c++
Même processus de compilation que la méthode précédente
Sim. Numérique – chapitre 5 - 17
Soucis de compilation ...
Troisième méthode : instanciation explicite
main.c++ vecteur.h vecteur.tem
#include ” v e c t e u r . h” #i f n d e f vectH #include ” v e c t e u r . h”
int main ( ) #define v ec t H
{ vec t <double> V; template <typename T> // i m p lé m e n t a t i o n
. . . class vect { . . . }; template <typename T>
} #endif . . .
vecteur.c++
#include ” v e c t e u r . tem”
vec t <double> vd ; Instanciation explicite des types permis
vec t <f l o a t > v f ;
. . .
Compilation de vecteur.c++ : génère les versions double et float
avec une seule version
Sim. Numérique – chapitre 5 - 18
Soucis de compilation ...
autres méthodes dépendantes du compilateur
voir les documentations
mot clé export
La norme C++ propose le mot clé export pour gérer ces problèmes
Un seul compilateur payant (Commeau) l’a implémenté !!!
lors d’une réunion de normalisation récente , il a été demandé de retirer
export de la norme … sans succès
En résumé : utiliser la première méthode (c’est la plus simple)
Sim. Numérique – chapitre 5 - 19
class complex de la STL
template <c l a s s T> c l a s s complex {
... // p r i v a t e d e f i n i t i o n
public :
typedef T v a l u e t y p e ;
complex ( const T& r e = T( ) , const T& im = T ( ) ) ;
complex ( const complex &);
template <c l a s s X> complex ( const complex<X>&);
T r e a l ( ) const ;
T imag ( ) const ;
complex<T>& operator= ( const T&);
complex<T>& operator+=(const T&);
complex<T>& operator−=(const T&);
complex<T>& operator∗=( const T&);
complex<T>& operator/=( const T&);
template <c l a s s X> complex<T>& operator= ( const complex<X>&);
template <c l a s s X> complex<T>& operator+= ( const complex<X>&);
template <c l a s s X> complex<T>& operator−= ( const complex<X>&);
template <c l a s s X> complex<T>& operator∗= ( const complex<X>&);
template <c l a s s X> complex<T>& operator/= ( const complex<X>&);
};
Sim. Numérique – chapitre 5 - 20
class complex de la STL
Opérateurs algébriques
template<c l a s s T> complex<T> operator+(const complex<T>&,
const complex<T>&);
template<c l a s s T> complex<T> operator+(const complex<T>&, T&);
template<c l a s s T> complex<T> operator+(T, const complex<T>&);
template<c l a s s T> complex<T> operator −(const complex<T>&,
const complex<T>&);
template<c l a s s T> complex<T> operator −(const complex<T>&, T&);
template<c l a s s T> complex<T> operator −(T, const complex<T>&);
template<c l a s s T> complex<T> operator ∗ ( const complex<T>&,
const complex<T>&);
template<c l a s s T> complex<T> operator ∗ ( const complex<T>&, T&);
template<c l a s s T> complex<T> operator ∗ (T, const complex<T>&);
template<c l a s s T> complex<T> operator / ( const complex<T>&,
const complex<T>&);
template<c l a s s T> complex<T> operator / ( const complex<T>&, T&);
template<c l a s s T> complex<T> operator / (T, const complex<T>&);
template<c l a s s T> complex<T> operator+(const complex<T>&);
template<c l a s s T> complex<T> operator −(const complex<T>&);
Sim. Numérique – chapitre 5 - 21
class complex de la STL
Opérateurs de comparaison et de flux
template<c l a s s T> bool operator==(const complex<T>&,
const complex<T>&);
template<c l a s s T> bool operator==(const complex<T>&, T&);
template<c l a s s T> bool operator==(T, const complex<T>&);
template<c l a s s T> bool operator !=( const complex<T>&,
const complex<T>&);
template<c l a s s T> bool operator !=( const complex<T>&, T&);
template<c l a s s T> bool operator != (T, const complex<T>&);
template <c l a s s T, c l a s s charT , c l a s s t r a i t s >
b a s i c i s t r e a m <charT , t r a i t s >&
operator>>(i s t r e a m &, complex<T>&);
template <c l a s s T, c l a s s charT , c l a s s t r a i t s >
b a s i c o s t r e a m <charT , t r a i t s >&
operator<<(ost ream &, const complex<T>&);
template<c l a s s T> T r e a l ( const complex<T>&);
template<c l a s s T> T imag ( const complex<T>&);
Sim. Numérique – chapitre 5 - 22
class complex de la STL
template<c l a s s T> T abs ( const complex<T>&);
template<c l a s s T> T a rg ( const complex<T>&);
template<c l a s s T> T norm ( const complex<T>&);
template<c l a s s T> complex<T> c o n j ( const complex<T>&);
template<c l a s s T> complex<T> p o l a r ( const T&, const T&);
template<c l a s s T> complex<T> c o s ( const complex<T>&);
template<c l a s s T> complex<T> co s h ( const complex<T>&);
template<c l a s s T> complex<T> exp ( const complex<T>&);
template<c l a s s T> complex<T> l o g ( const complex<T>&);
template<c l a s s T> complex<T> l o g 1 0 ( const complex<T>&);
template<c l a s s T> complex<T> pow ( const complex<T>&, i n t ) ;
template<c l a s s T> complex<T> pow ( const complex<T>&, T&);
template<c l a s s T> complex<T> pow ( const complex<T>&,
const complex<T>&);
template<c l a s s T> complex<T> pow ( const T&,const complex<T>&);
template<c l a s s T> complex<T> sin ( const complex<T>&);
template<c l a s s T> complex<T> sinh ( const complex<T>&);
template<c l a s s T> complex<T> sqrt ( const complex<T>&);
template<c l a s s T> complex<T> tan ( const complex<T>&);
template<c l a s s T> complex<T> tanh ( const complex<T>&);
Sim. Numérique – chapitre 5 - 23
Résumé
Modèle de fonctions et de classe
Ne satisfait pas au paradigme de l'implémentation séparée
Mécanisme puissant d'abstraction
Assez facile à mettre en oeuvre
Largement utilisé par la STL et la communauté
Sim. Numérique – chapitre 5 - 24