Notre page Facebook | |
Notre groupe Facebook |
|_Accueil Langage C++
|_Définition de templates
|_Fonction template
|_Classe template
|_Utilisation du mot clé typename
|_Spécialisation de templates
|_Utilisation de types traits
|_Les nouveautés C++ 2011
La notion de type de trait permet, via la specialisation de template, d'ajouter des informations, de manière non intrusive, à un type de données.
A titre d'exemple, nous allons développer un type de trait permettant de choisir (à la compilation) si, lors de passage de paramètres à certaines fonctions,
un type de données doit être pris par valeur ou par référence Dans notre exemple, c'est la fonction mini
qui, via le template, pourra être invoquée sur n'importe
quel type de données pour peu qu'il fournisse un opérateur <. Si la taille du type utilisé par le template est plus grande que la taille d'un pointeur, on
préferrera passer les paramètres par références. Par contre, si la taille du type utilisé pour les paramètres du template et inférieure ou égale à celle d'un pointeur, on
préferrera passer les paramètres par valeurs. Voici comment coder une tel type de trait.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <iostream> #include <string> using namespace std; template <typename T> struct ParameterTraits { template <typename PT, bool byValOrByRef> struct ParameterTraitsImpl; template <typename PT> struct ParameterTraitsImpl<PT, true> { typedef const PT & Type; }; template <typename PT> struct ParameterTraitsImpl<PT, false> { typedef PT Type; }; typedef typename ParameterTraitsImpl<T, (sizeof(T) > sizeof(void *))>::Type ParameterType; }; template <typename T> T mini( typename ParameterTraits<T>::ParameterType X, typename ParameterTraits<T>::ParameterType Y ) { return X < Y ? X : Y; } int main() { cout << "string=" << sizeof(string) << " - double=" << sizeof(double) << " - void *=" << sizeof(void *) << endl; string s1 = "First"; string s2 = "Second"; cout << mini<string>(s1,s2) << endl; // passage des paramètres par références constantes double d1 = 8.123456789; double d2 = 3.141592654; cout << mini<double>( d1, d2 ) << endl; // passage des paramètres par valeurs return 0; } |
La spécialisation de template est réaliser dans les sous-types définit à l'intérieur de la structure ParameterTraits (ils se nomment tous ParameterTraitImpl, vu qu'il s'agit de spécilisation de template).
Comme seulement deux cas nous intéressent (type plus petit ou égal que la taille d'un pointeur ou non) un booléen suffira à différencier les deux cas de spécialisation. Ensuite, le choix définitif de la
spécialisation la plus adaptée au type T
, sera réalisé au niveau du typedef
. Dans la fonction main
, deux cas sont envisagés : l'utilisation de la fonction template avec des
std::string
et l'utilisation de la même fonction avec des doubles. Dans le premier cas, les paramètres seront donc passés par références. Dans le second cas, ils seront passés par valeurs.
Pour compiler et exécuter votre programme, veuillez procéder ainsi :
$> g++ -o Essai Essai.cpp $> Essai string=32 - double=8 - void *=8 First 3.14159 $>
Le standard C++ 2011 propose l'entête <type_traits>
: elle définit un grand nombre de type de traits prèt à l'emploi. A titre d'exemple, voici un exemple de l'utilisation du trait
is_same
: il permet de comparer, à la compilation, deux types de données et de savoir s'ils sont équivalents. Comme un trait est évalué à la compilation, il peut parfois être utilisé dans un
static_assert
: c'est le cas dans l'exemple suivant qui permet de limiter l'utilisation d'un template à quatre cas uniques (char et short, signés ou non).
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 |
#include <iostream> #include <type_traits> using namespace std; template <typename CHAR_TYPE> class MyString { CHAR_TYPE * characters; public: MyString( const CHAR_TYPE * characters = nullptr ) { static_assert( is_same<CHAR_TYPE, char>::value || is_same<CHAR_TYPE, unsigned char>::value || is_same<CHAR_TYPE, short>::value || is_same<CHAR_TYPE, unsigned short>::value, "Bad template type" ); // TODO: finish this class } // TODO: finish this clas }; int main() { MyString<char> s1( "Hello" ); // Compilation is OK //double d = 3.5; //MyString<double> s2( &d ); // Compilation is KO return 0; } |
Pour compiler et exécuter votre programme, veuillez procéder comme indiqué ci dessous. Il est a noter que nous avons volontairement tenter de compiler une utilisation de notre template avec le type double : une erreur de compilation est donc naturellement produite.
$> g++ -o Essai Essai.cpp Essai.cpp : dans l'instantiation de 'MyString<CHAR_TYPE>::MyString(const CHAR_TYPE*) [with CHAR_TYPE = double]' : Essai.cpp:26:29: required from here Essai.cpp:11:2: erreur: static assertion failed: Bad template type static_assert( is_same<CHAR_TYPE, char>::value || is_same<CHAR_TYPE, unsigned ^ $>
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :