• Aucun résultat trouvé

SPÉCIALISATION DES OBJETS PARAMÉTRÉS

13. LES MODÈLES PARAMÉTRÉS (TEMPLATE)

13.5 SPÉCIALISATION DES OBJETS PARAMÉTRÉS

Jusqu'à présent, les classes et les fonctions paramétrées sont définis d'une manière unique, pour tous les types valeurs des arguments paramétrés. Cependant, dans certaines situations, il peut être utile de définir une version spécifique d'une classe ou d'une fonction pour un jeu donné de arguments paramétrés.

Exemple

La pile définie dans les exemples précédents peut être implémentée plus efficacement si elle stocke des pointeurs. Il suffit que la méthode pop retourne un objet.

Définition

Une fonction ou une classe paramétrée peut être spécialisée pour un jeu donné d'arguments paramétrés.

Deux types de spécialisation sont définis : les spécialisations partielles, pour lesquelles quelques arguments paramétrés ont une valeur fixée, et les spécialisations totales pour lesquelles tous les arguments paramétrés ont une valeur déterminée.

13.5.1 Spécialisation partielle d'une classe

Objet

Une spécialisation partielle permet de définir l'implémentation d'une fonction ou d'une classe paramétrée pour des valeurs fixées de certains de ses arguments paramétrés dont la nature peut varier (par exemple un pointeur) pour imposer au compilateur le choix de l'implémentation correspondante.

Syntaxe

La liste des arguments paramétrés, préalablement déclarés, est spécifiée entre les opérateurs de comparaison à la définition de la classe ou de la fonction paramétré.

Exemple

#include <iostream.h> // Exemple de spécialisation partielle // Définition d'une classe paramétrée

template <class T1, class T2, int I>

class A

{public : T1 champ1;

A(){cout << " 0 " << endl;}

};

182

template <class T, int I>

class A<T, T*, I> // Spécialisation 1

{public: A(){cout << " 1 " << endl;}};

template <class T1, class T2, int I>

class A<T1*, T2, I> // Spécialisation 2

{public: A(){cout << " 2 " << endl;}};

template <class T>

class A<T*, int, 5> // Spécialisation 3

{public: A(){cout << " 3 " << endl;}};

template <class T1, class T2, int I>

class A<T1, T2*, I> // Spécialisation 4

{public: A(){cout << " 4 " << endl;}};

template <class T2, int I>

class A<T2, int, I> // Spécialisation 5

{public: A(){cout << " 5 " << endl;}};

int main(void)

{A<int,float,4> essai;

A<float, int, 6> essai2;

A<int, int*, 2> essai3;

A<char*,double,6> essai4;

A<int,float*, 7> essai5;

A<char*,int,5> essai6;

}

// résultat 0

5 1 2 4 3

Règles d'utilisation

Les spécialisations doivent respecter les règles suivantes :

 Le nombre des arguments paramétrés déclarés à la suite du mot-clé template peut varier.

 Le nombre de valeurs spécialisées doit rester constant (dans l'exemple précédent, il y en a trois).

183

 Un arguments ne peut pas être exprimée en fonction d'un autre arguments paramétré de la spécialisation.

 Le type d'une des valeurs de la spécialisation ne peut pas dépendre d'un autre arguments.

 La liste des arguments de la spécialisation ne doit pas être identique à la liste implicite de la déclaration template correspondante.

 La déclaration de la liste des arguments template d'une spécialisation ne doit pas contenir des valeurs par défaut inutilisables.

Exemple 1 template <int I, int J>

struct B {};

template <int I>

struct B<I, I*2> // Erreur ! Spécialisation incorrecte {};

Exemple 2

template <class T, T t>

struct C {};

template <class T>

struct C<T, 1>; // Erreur! Spécialisation incorrecte!

13.5.2 Spécialisation totale d'une classe

Syntaxe

 La spécialisation totale impose de fournir une liste vide de arguments paramétrés entre les opérateurs de comparaison, après l'identificateur de la fonction ou de la classe paramétrée.

 La définition de cette fonction ou classe doit être précédée de l’instruction : template <>

Exemple 1

Soit la fonction Min définie plus haut, utilisée sur une structure Structure et devant exploiter un de ses champs pour effectuer les comparaisons. Elle peut être spécialisée de la manière suivante :

// Spécialisation totale struct Structure

{int Clef; // Clef d'accès aux données.

void *pData; // Pointeur sur les données.

};

template <>

Structure Min<Structure>(Structure s1, Structure s2) {if (s1.Clef<s2.Clef) return s1; else return s2;}

184

Remarque

Certains compilateurs n'acceptent pas la liste vide des arguments template ce qui n'est pas portable.

13.5.3 Spécialisation d'une méthode d'une classe paramétrée

La spécialisation partielle d'une classe peut parfois être assez lourde, en particulier si la structure de données qu'elle contient est identique dans les différentes versions spécialisées. Dans ce cas, il peut être plus simple de ne spécialiser que certaines méthodes ce qui permet d'éviter de redéfinir les données membres non concernées.

Syntaxe

La méthode est spécialisée par la définition de certains de ses arguments paramétrés.

Exemple

// méthode spécialisée d’une classe paramétrée

#include <iostream.h>

template <class T>

class Item {public : T item;

public:

Item(){item=(T)0;} // Constructeur par défaut Item(T);

void set(T);

T get(void) const;

void print(void) const;

};

template <class T>

Item<T>::Item(T i) // Constructeur {item = i;}

// Accesseurs template <class T>

void Item<T>::set(T i) {item = i;

cout << "set : item = " << item << endl;

}

template <class T>

T Item<T>::get(void) const

{cout << "get : item " << item << endl;return item;}

// Fonction d'affichage paramétré :

185

template <class T>

void Item<T>::print(void) const

{cout << "print paramétré : " << item << endl;}

// Fonction d'affichage spécialisée pour le type int * et la méthode print template <>

void Item<int *>::print(void) const {cout << *item << endl;}

int main(void) {int a=8,b,*pa=&a;

float pi=3.1416;

Item <int> entier;

Item <float> flottant(pi);

Item <int *> pointeur(pa);

entier.set(a);

entier.get();

entier.print();

flottant.get();

flottant.print();

pointeur.get();

pointeur.print();

}

// résultat set : item = 8 get : item 8

print paramétré : 8 get : item 3.1416

print paramétré : 3.1416 get : item 0xbffff9d4 8