Rechercher
 
Notre page Facebook
Notre groupe Facebook


Android Game
E-Reversi - Android Game

Utilisation de types traits

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.

Définition d'un type de trait

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 byConstRef> 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;
}
Mise en oeuvre et utilisation d'un type de trait

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
$>

Types de traits fournies par C++ 2011

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'il 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;
}
Utilisation d'un type de trait standard C++ 2011

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 
  ^
$>