Template (programmation)

Template (programmation)

En programmation informatique, les templates (en anglais modèles, parfois aussi appelés patrons) sont une particularité de la programmation en langage C++, qui autorise l'écriture d'un code sans considération envers le type des données avec lesquelles il sera finalement utilisé. Les templates introduisent le concept de programmation générique dans le langage.

Les templates sont d'une grande utilité pour les programmeurs en C++, plus particulièrement en les combinant avec l'héritage multiple, la surcharge d'opérateur ou plus généralement la programmation orientée objet. La bibliothèque standard de C++ fournit de nombreux outils utiles dans un cadre de travail fait avec les templates, en particulier dans la STL.

Le mécanisme des templates a aussi été inclus dans d'autres langages objet comme Java, mais a une signification différente lors de la compilation, puisqu'il s'appuie sur la super-classe Object du langage.

Sommaire

Intérêt

Les templates permettent à une classe (class, struct ou union) ou une fonction de pouvoir s'adapter à plusieurs types sans avoir besoin d'être recopiée ou surdéfinie.

Les templates sont une alternative aux macros préprocesseur, déconseillées en C++. Certaines d'entre elles pourront donc être mises à niveau ; par exemple, la macro[1] :

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

pourra être remplacée en C++ par le patron de fonction[2] :

template <typename T>
T min (const T &x, const T &y)
{ return (x<y)? x:y; }

De plus, en programmation orientée objet, il arrive souvent que l'on veuille écrire une classe sans considérer les types, ou certaines valeurs. Avec les langages non objet, il était toujours possible de recopier la fonction en changeant les types là où c'est nécessaire, et ce pour chaque type de base. Seulement, la POO permet l'ajout de types définis par l'utilisateur qui ne peuvent pas être prévus. Les templates sont apparus pour pallier le problème.

Aperçu technique au travers d'exemples

Déclaration d'un patron

La déclaration d'une classe ou fonction générique doit être précédée par :

template <typename T, typename U>

Le mot clé class peut être utilisé à la place de typename. Les types T et U pourront alors être utilisés dans le corps de la classe ou de la fonction.

Avec les classes, on peut aussi se servir de la généricité pour utiliser une valeur constante :

template <typename T, int N>

La valeur entière N pourra être utilisée dans la classe comme s'il s'agissait d'un entier constant.

Chaque type ou valeur déclaré peut posséder une valeur par défaut, comme les arguments muets d'une fonction.

Il y a deux types de patrons.

Les patrons de fonction

Exemple : fonction min<T>(T,T)

Un patron de fonction pourra supporter n'importe quel type pour ses arguments. Par exemple :

template <typename T>
T min (const T &x, const T &y)
{ return (x<y)? x:y; }

Appel de la fonction

Voici un exemple d'utilisation :

#include <iostream>
 
int main ()
{
    // Appel de min<int>
    std::cout << min(-20, 5) << ' ';
    // Appel de min<double>
    std::cout << min(6.4, -17.3) << ' ';
    // Appel de min<double> ; le type doit être précisé pour pallier l'ambiguïté
    std::cout << min<double>(6.96e2, 48) << std::endl;
 
    return 0;
}

Le programme affichera la ligne suivante :

-20 -17.3 48

Le type T est défini par les paramètres donnés à la fonction min. Une fonction générique doit donc comporter un argument de chacun des types qu'elle compte utiliser pour pouvoir être compilée.

Cependant, si plusieurs arguments définissent un même type T pour la fonction mais ne sont pas du même type, le type utilisé doit être explicitement indiqué (les arguments seront donc convertis dans ce type si nécessaire).

Dans la bibliothèque standard

Dans la STL, on trouve des patrons de fonction en particulier avec les algorithmes du fichier d'en-tête <algorithm>. Par exemple :

  • extrema : std::min, std::max ;
  • compter/chercher : std::count, std::find ;
  • modification : std::copy, std::fill, std::swap ;
  • tri : std::sort

Les patrons de classe

Exemple : classe Tab<T,N>

Déclarer une classe comme patron lui permet en particulier de pouvoir posséder des membres d'un type défini par l'utilisateur. Par exemple[3],[4] :

#include <cstdarg>
 
template <typename T, int N>
class Tab
{
    /* Membres */
        // Ici est stocké le tableau avec N éléments du type T
        T tab[N];
        // Utile pour l'affichage
        const char *separateur;
 
    public :
    /* Constructeurs */
        // Constructeur par défaut
        Tab<T,N> (const T &t=0) : separateur(" ")
        {
            for (int i=0; i<N; i++)
                tab[i]=t;
        }
        // Ce constructeur permet d'initialiser les éléments du tableau
        Tab<T,N> (const T &t1, const T &t2, ...) : separateur(" ")
        {
            tab[0]=t1, tab[1]=t2;
            va_list args;
            va_start (args,t2);
            for (int i=2; i<N; i++)
              tab[i]=va_arg(args,T);
            va_end (args);
        }
        // Constructeur par recopie (notez qu'il s'agit de celui par défaut)
        Tab<T,N> (const Tab<T,N> &t) : tab(t.tab), separateur(t.separateur)
        {}
        // Surdéfinition de l'opérateur d'affectation (notez qu'il s'agit de celle par défaut)
        Tab<T,N> &operator= (const Tab<T,N> &t)
        {
            for (int i=0; i<N; i++)
                tab[i]=t.tab[i];
            return *this;
        }
 
    /* Fonctions d'accès et d'altération */
        int size () const
        { return N; }
        const char *obt_sep () const
        { return separateur; }
        void config_sep (const char *nouv_sep)
        { separateur=nouv_sep; }
        const Tab<T,N> &operator() (const char *nouv_sep)
        { separateur=nouv_sep; return *this; }
        T &operator[] (int i)
        { return tab[i]; }
        const T &operator[] (int i) const
        { return tab[i]; }
        template <int N2> operator Tab<T,N2> ()
        {
                Tab<T,N2> t;
                for (int i=0; i<((N<N2)? N:N2); i++)
                        t.tab[i]=tab[i];
                return t;
        }
};

La notation Tab<T,N> est ici redondante et pourrait être remplacée simplement par Tab.

Déclaration d'objets de la classe

Voici un exemple d'utilisation :

#include <iostream>
#include <algorithm>
 
// Surcharge de l'opérateur de décalage binaire vers la gauche
 // pour pouvoir envoyer nos tableaux sur un flot de sortie
template <typename T, int N>
std::ostream &operator<< (std::ostream &sortie, const Tab<T,N> &tab)
{
    for (int i=0; i<N; i++)
        sortie << tab[i] << ((i<N-1)? tab.obt_sep():"");
}
 
int main ()
{
    /* Deux listes de cinq flottants */
    Tab<double,5> liste1, liste2(66.17,4.3e3,22e5,1e-4,liste1[4]);
    liste1=liste2;
    for (int i=0; i<liste1.size(); i++)
        liste1[i]*=1.5;
    std::cout << liste1 << std::endl;
 
    /* Des tirets pour séparer */
    std::cout << Tab<char,37>('-')("") << std::endl;
 
    /* Calculs sur un tableau à deux dimensions (19x19) : création de dix carrés imbriqués */
    Tab<Tab<int,19>,19> carres;
    for (int i=0; i<=carres.size()/2; i++)
        for (int j=0; j<carres[i].size(); j++)
            carres[i][j]=std::max(9-i,std::abs(9-j));
    for (int i=0; i<carres.size(); i++)
        carres[18-i]=carres[i];
    carres.config_sep("\n");
    std::cout << carres << std::endl;
}

Ceci affichera  :

99.255 6450 3.3e+06 0.00015 0
-------------------------------------
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9
9 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 9
9 8 7 6 6 6 6 6 6 6 6 6 6 6 6 6 7 8 9
9 8 7 6 5 5 5 5 5 5 5 5 5 5 5 6 7 8 9
9 8 7 6 5 4 4 4 4 4 4 4 4 4 5 6 7 8 9
9 8 7 6 5 4 3 3 3 3 3 3 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 2 2 2 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 1 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 1 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 2 2 2 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 3 3 3 3 3 3 4 5 6 7 8 9
9 8 7 6 5 4 4 4 4 4 4 4 4 4 5 6 7 8 9
9 8 7 6 5 5 5 5 5 5 5 5 5 5 5 6 7 8 9
9 8 7 6 6 6 6 6 6 6 6 6 6 6 6 6 7 8 9
9 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 9
9 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9

Les arguments du patron sont donc ici indiqués entre chevrons après le nom de la classe.

Attention lors de l'imbrication de noms de classes patron : les chevrons doivent être séparés par un caractère d'espacement, sinon ils seront interprétés comme l'opérateur de décalage binaire et la compilation échouera. Par exemple, un vecteur de vecteurs d'entiers doit être déclaré de cette manière :

#include <vector>
 
std::vector<std::vector<int> > matrix;

Ce problème mineur devrait être résolu dans la prochaine norme du C++, C++1x.

Dans la bibliothèque standard

Dans la STL, on en rencontre en particulier avec les conteneurs, comme :

  • séquentiels : std::vector, std::deque et std::list ;
  • associatifs : std::map et std::set ;

mais aussi dans bien d'autres domaines, tels que :

  • les couples de variables : std::pair de <utility> ;
  • les nombreux std::basic_* (istream, ostream, string…)…

Compilation des patrons

Classes patron

À chaque fois qu'un patron de classe est utilisé avec de nouveaux arguments, une nouvelle classe patron (attention à bien différencier les deux concepts) est compilée. Ainsi, la déclaration d'un objet de type std::vector<int> et d'un autre de type std::vector<double> instanciera deux classes différentes. Les objets seront donc de types différents, sans qu'aucun lien puisse être fait entre les deux (pas même une conversion).

Fonctions patron

Le comportement est similaire avec les fonctions : chaque appel d'un patron de fonction avec de nouveaux types d'arguments compile une nouvelle fonction patron.

Les templates dans l'édition de liens

Les patrons de classes et de fonctions doivent être inclus dans tous les fichiers qui les utilisent. Ils ne sont donc pas pris en compte lors de l'édition de liens, bien que seule une classe/fonction patron de chaque type soit finalement incluse dans l'exécutable.

La prochaine norme, C++1x, devrait permettre de déclarer des templates externes.

Templates variadiques

C++1x introduit les templates variadiques qui, à l'image des fonctions variadiques du langage C, peuvent supporter un nombre variable de types.

Les std::tuple déjà présents dans Boost en seront l'illustration la plus basique.

Notes

  1. Définie dans de nombreuses bibliothèques non standard, comme <windows.h>.
  2. Défini dans <algorithm>.
  3. Ces codes sont testés.
  4. Voir fonction variadique pour une explication du mécanisme du second constructeur. Voir aussi la surcharge des opérateurs pour une explication du mécanisme d'indexation et d'affectation.



Wikimedia Foundation. 2010.

Contenu soumis à la licence CC-BY-SA. Source : Article Template (programmation) de Wikipédia en français (auteurs)

Игры ⚽ Поможем сделать НИР

Regardez d'autres dictionnaires:

  • Template — Gabarit (mise en page) Un gabarit, souvent nommé en informatique Template (anglicisme utilisé en informatique pour désigner un modèle de conception de logiciel ou de présentation des données) est un patron de mise en page où l on place images et… …   Wikipédia en Français

  • Programmation générique — Généricité En programmation, la généricité d une fonction repose sur son indépendance vis à vis du type, et éventuellement du nombre, de ses arguments. C est un concept important pour un langage de haut niveau car il permet d augmenter le niveau… …   Wikipédia en Français

  • Langage de programmation C plus plus — C++ Apparu en 1985 (dernière révision en 2003) Auteur Bjarne Stroustrup …   Wikipédia en Français

  • Standard Template Library — Pour les articles homonymes, voir STL. La Standard Template Library (STL) est une bibliothèque C++, normalisée par l ISO (document ISO/CEI 14882) et mise en œuvre à l aide des templates. Cette bibliothèque fournit : un ensemble de classes… …   Wikipédia en Français

  • Matrix Template Library — La Matrix Template Library (MTL) est une bibliothèque d algèbre linéaire pour les programmes C++. La MTL utilise la programmation générique, qui réduit considérablement la longueur des codes. Toutes les matrices et vecteurs sont disponibles dans… …   Wikipédia en Français

  • Interface De Programmation — Pour les articles homonymes, voir API. Une interface de programmation (Application Programming Interface ou API) est un ensemble de fonctions, procédures ou classes mises à disposition des programmes informatiques par une bibliothèque logicielle …   Wikipédia en Français

  • Interfaces de programmation — Interface de programmation Pour les articles homonymes, voir API. Une interface de programmation (Application Programming Interface ou API) est un ensemble de fonctions, procédures ou classes mises à disposition des programmes informatiques par… …   Wikipédia en Français

  • Langage De Programmation Exotique — Un langage de programmation exotique est un langage de programmation imaginé comme un test des limites de la création de langages de programmation, un exercice intellectuel ou encore une blague, sans aucune intention de créer un langage… …   Wikipédia en Français

  • Langage de programmation ésotérique — Langage de programmation exotique Un langage de programmation exotique est un langage de programmation imaginé comme un test des limites de la création de langages de programmation, un exercice intellectuel ou encore une blague, sans aucune… …   Wikipédia en Français

  • Interface de programmation — Pour les articles homonymes, voir API. Une interface de programmation (Application Programming Interface ou API) est une interface fournie par un programme informatique. Elle permet l interaction des programmes les uns avec les autres, de manière …   Wikipédia en Français

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”