IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Généricité : template / typedef


Sujet :

C++

  1. #1
    Membre émérite

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Billets dans le blog
    1
    Par défaut Généricité : template / typedef
    Bonjour !

    Cette discussion m'a semblé importante, je n'arrive cependant pas à comprendre les subtilités.

    Cela demande un peu de réflexion pour y arriver, mais, une fois que tu penses à utiliser typedef le principe est cependant simplissime
    J'ai beau "réflexionner", ces typedef, ils servent à quoi ?!

    Le code n'est pas complet mais j'imagine la suite ainsi (dites moi si je me trompes)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    struct Point2DFlag{};
    struct Point3DFlag{};
     
    template <typename Type, typename Flag>
    class Point;
     
    template <typename Type>
    class Point<Type, Point2DFlag>
    {
        public:
            typedef Type value_type;//Ils permettent de récupérer sur quel type on travaille ?
            typedef Point2DFlag flag_type;//Et sous quelle forme ?
            typedef Point<value_type, flag_type> point_type;//Là je vois pas trop...
            //Ensuite ce n'est que supposition :
            Point<Type, Point2DFlag>( const value_type x, const value_type y ) : x( x ), y( y ) {}
            const value_type GetX() const { return x; }
            const value_type GetY() const { return y; }
     
        private:
            value_type x;
            value_type y;
    };
    Pourquoi ne pas faire simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    struct Point2DFlag{};
    struct Point3DFlag{};
     
    template <typename Type, typename Flag>
    class Point;
     
    template <typename Type>
    class Point<Type, Point2DFlag>
    {
        public:
            Point( const Type x, const Type y ) : x( x ), y( y ) {}
            const Type GetX() const { return x; }
            const Type GetY() const { return y; }
     
        private:
            Type x;
            Type y;
    };
    Je ne vois pas vraiment la différence..

    La suite me pose aussi problème, j'y reviendrais sans doute après.

  2. #2
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Parce qu'avec le premier, value_type est un identifier existant, tandis qu'avec le second, Type n'existe que le temps de l'instanciation (quasiment comme une "macro locale").

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Par défaut
    Imagine que tu ais une fonction f qui doit renvoyer le x du type de point utilisé pour instancier ton point. Dans le premier cas, tu peux faire un truc du style:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<typename point_t>
    typename point_t::value_type f(point_t const& p) { return p.GetX(); }
    et l'appeler comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Point<double, Point2DFlag> p;
    double res = f(p);
    alors que tu ne peux pas faire ça avec la deuxième approche, tu ne pourras pas définir f (tu n'as pas "accès" au type instanciant ta classe comme l'a dit leternel)

  4. #4
    Membre émérite

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Billets dans le blog
    1
    Par défaut
    Hoooo
    Merci messieurs !!!
    Ça faisait un bon moment que ça me perturbais, l'exemple fût la révélation !
    Et je comprends maintenant ce qu'a dit leternel

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,
    En fait, un typedef fournit un nouvel identifiant qui est un alias du type envisagé.

    On les utilises généralement pour simplifier l'écriture du code.

    Sais tu par exemple, que la classe std::string est en réalité un typedef de
    std::basic_string<char>

    Il en est déjà qui envisagent de placer une directive using namespace std; pour ne pas avoir à écrire de manière systématique std::string. Imagines tu leur tristesse à l'idée d'avoir à écrire systématiquement std::basic_string<char> chaque fois qu'ils voudront déclarer une variable de type chaine de caractère ([EDIT] et je ne te parle pas des flux qui sont généralement des typedef héritant de std::basic_*stream<char, trait> [/EDIT])

    Dans la discussion que tu cites, on veut pouvoir travailler avec des points de beaucoup de sortes différentes:
    Des points 2D (avec les axes x et y) et des points 3D (avec les axes x, y et z) dont les valeurs de x, y et (éventuellement) z pourraient être représentées par n'importe quel type susceptible de représenter une valeur numérique.
    Si l'on s'en tient "simplement" aux types primitifs, cela correspond à 13 types (cinq primitifs "entiers", en version signée et non signée + 3 primitifs "réels" )de valeur numérique différentes, pour les points 2D, et autant pour les points 3D.

    Au final, en s'en tenant au seuls types primitifs, on voudra pouvoir travailler avec quelques 26 sortes de points différents, et l'on ne sait pas, au moment où les classes sont développées, quel sera le type de point qui sera utilisé.

    Seulement, en informatique, toute classe, toute fonction, toute variable ne vaut jamais que par l'utilisation qui en est faite: une classe, une fonction, une variable qui est définie mais qui n'est jamais utilisée n'a strictement aucun intérêt

    La première utilisation qui sera faite de ces types de points sera de les utiliser pour représenter un bipoint : une paire de points qui permet de représenter un rectangle (si on travaille en 2D) ou un volume (si on travaille en 3D) à partir du moment où les points sont des point opposés.

    Nous aurons donc de nouveau 26 sortes différents de bipoint, pour lesquelles nous devrons pouvoir savoir le type utilisé pour représenter les valeurs et si l'on travaille en 2D ou en 3D.

    Cela signifie que, à l'intérieur de la classe bipoint (et de ses fonctions membres essentiellement), chaque fois que je veux déclarer une variable de type point, il faut que j'écrive quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point<Type, Flag> mavar(/* paramètres requis*/);
    et que je sache le type du point renvoyé par toutes les fonctions qui renvoient un point.

    C'est à dire que je devrai faire la gymnastique mentale, si je manipule un bipoint<double, Point3D>, de me dire que le point qui sera renvoyé est de type point<double, Point3D> .

    Evidemment, ca signifie aussi que mon code devra ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    point<double, Point3D> p = monBipoint.laFonction();
    Pour l'instant, ca reste encore simple.

    Mais rajoutons maintenant la classe qui manipule un bipoint, dont on ignore à peu près tout, et qui ne nécessite pas de spécialisation pour travailler en 2D ou en 3D.

    Je pourrais parfaitement écrire ma classe sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename BP> //BP: paramètre template qui représente "n'importe quel type" de bipoint
    class MaClasse{
     
        private:
            BP bp_;
    };
    Ca semble marcher...

    Jusqu'au moment où je vais devoir manipuler un point renvoyé par une des fonctions de bipoint, voire, la valeur de l'axe x d'un de ce point.

    Parce que, à ce moment là, je fais comment, moi, pour récupérer le type du point, ou le type qui représente la valeur

    Si je complete cette classe sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename BP> //BP: paramètre template qui représente "n'importe quel type" de bipoint
    class MaClasse{
        public:
            void foo(){
                ??? pointTemp = bp.topLeft();
            }
        private:
            BP bp_;
    };
    Je mets quoi comme type pour pointTemp

    Je n'ai aucun moyen de le savoir
    C'est là que les typedefs deviennent intéressants, car je sais que, quel que soit le type du bipoint réel qui aura été utilisé pour instancier cette classe, il disposera d'un typedef nommé "point_type" qui correspondra au type de point équivalent.

    Je pourrai donc arriver à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename BP> //BP: paramètre template qui représente "n'importe quel type" de bipoint
    class MaClasse{
        public:
            void foo(){
                typename BP::point_type pointTemp = bp.topLeft();
            }
        private:
            BP bp_;
    };
    Evidemment, si je viens à créer une classe template qui manipule un point (sans précision), je me retrouverais confronté au même problème au moment où je devrais faire la distinction entre un point 2D et un point 3D ainsi qu'au moment où je voudrais récupérer la valeur sur l'axe des x, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename PT> // PT: n'importe quel type de point
    class AutreClasse{
        public:
            void bar{
                ??? tempX = pt_.getX();
                /* avec le typedef dans la classe point, je peux écrire
                typename PT::value_type tempX = pt_.getX();
                */
            }
        private:
            PT pt_;
    };
    Pour la suite, bah, autant se laisser aller à sa fainéantise naturelle, d'autant plus que l'on risque, au moment de développer MaClasse (ou AutreClasse) de ne pas avoir une connaissance parfaite de tous les cas d'utilisation de cette classe.

    Mais il y a une chose qui est sure: on risque d'avoir très régulièrement besoin du type de point dans MaClasse et du type de valeur dans AutreClasse, simplement parce que ce sont des information auxquelles on peut avoir accès depuis l'un des membres de ces classes.

    Et il faut avouer que c'est "fatiguant" d'écrire systématiquement typename BP::point_type ou typename PT::value_type.

    On va donc se faciliter la vie en le faisant une bonne fois pour toute en... déclarant un nouveau typedef !

    Au final, on pourra se trouver avec des classes proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    template <typename BP> //BP: paramètre template qui représente "n'importe quel type" de bipoint
    class MaClasse{
        public:
            /* pour que les classe qui l'utilisent puissent récupérer le type du bipoint */
            typedef BP bipoint_type;
            /* Le type de point correspondant */
            typedef typename BP::point_type point_type;
            /* et les type des valeurs et des flags correspondants */
            typedef typename point_type::value_type value_type;
            typedef typename point_type::flag_type flag_type;
            void foo(){
                point_type pointTemp = bp.topLeft();
            }
        private:
            BP bp_;
    };
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    template <typename PT> // PT: n'importe quel type de point
    class AutreClasse{
        public:
            /* pour que les utilisateurs de AutreClasse puissent récupérer le type de point
             */
            typedef PT point_type;
            /* et les type des valeurs et des flags correspondants */
            typedef typename point_type::value_type value_type;
            typedef typename point_type::flag_type flag_type;
            void bar{
                value_type tempX = pt_.getX();
            }
        private:
            PT pt_;
    };
    Au final, on pourrait dire que les typedef n'ont qu'un intérêt "relativement limité" (dans le sens où cela facilite quand même l'écriture du code) à l'intérieur de la classe en elle-même, mais que tout leur intérêt sera, surtout, de fournir des informations dont on ne dispose pas *forcément* lorsqu'on devra manipuler nos variables
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre émérite

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Billets dans le blog
    1
    Par défaut
    Super ! Un nouveau roman !

    Merci beaucoup pour toutes ces précisions !

  7. #7
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Juste un point de détail : préférer maintenant (C++11) la syntaxe avec using au lieu de typedef :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typedef int MonInt;
    using MonInt = int;

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par PilloBuenaGente Voir le message
    Super ! Un nouveau roman !
    Oui, que veux tu j'ai souvent tendance à me laisser emporter par ma verve
    Merci beaucoup pour toutes ces précisions !
    Mais, de rien
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Les template typedefs existent ils ?
    Par SalutAVous dans le forum Langage
    Réponses: 5
    Dernier message: 10/12/2012, 22h55
  2. Réponses: 15
    Dernier message: 12/08/2012, 13h10
  3. [Généricité] Template design
    Par Trademark dans le forum Langage
    Réponses: 5
    Dernier message: 03/03/2012, 18h55
  4. Réponses: 5
    Dernier message: 04/11/2010, 11h01
  5. Template typedef, gni :/
    Par Groove dans le forum Langage
    Réponses: 5
    Dernier message: 22/02/2007, 19h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo