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 :

expression template et constructeur par défaut


Sujet :

C++

  1. #1
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut expression template et constructeur par défaut
    Bonjour à tous.

    Je suis en train de m'amuser à construire quelques expressions templates en me basant sur l'article http://www.angelikalanger.com/Articl...nTemplates.htm

    avec quelques ajouts de C++11.
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
     
    #include <iostream> 
     
    template <typename T> 
    class Expression 
    { 
        const T& expr; 
    public: 
        Expression(const T& t2):expr(t2){} 
        typename T::result operator()() 
        { 
            std::cout<<"expr eval"<<std::endl;     
            expr(); 
        } 
    }; 
     
    template <class T> Expression<T> makeExpression(const T& t) 
    { 
        std::cout<<"make expr"<<std::endl; 
        return Expression<T>(t); 
    } 
     
    template <class T> class Var 
    { 
        T t; 
    public : 
        Var(const Var& o):t(o.t){std::cout<<"copy"<<std::endl;} 
        Var(T t2):t(t2){std::cout<<"value "<<t<<std::endl;} 
        Var():t(T()){} 
        Var(Var&& o):t(std::move(o.t)){std::cout<<"move"<<std::endl;} 
        T operator()() const 
        { 
            return t; 
        } 
     
        Var& operator=(T o) 
        { 
            t=o; 
            return *this; 
        } 
        ~Var(){std::cout<<"dst"<<std::endl;} 
        typedef T result; 
    }; 
     
    template <typename T,typename U,typename Op> class ExpressionBin 
    { 
        const T& t; 
        const U& u; 
        Op op; 
    public: 
        ExpressionBin(){std::cout<<"wtf"<<std::endl;} // <<LA 
     
        ExpressionBin(const T& tt,const U& uu): 
        t(tt), 
        u(uu),op(Op()) 
        { 
     
        } 
     
        typedef decltype(op(T(),U())) result; 
        result operator()() const 
        { 
            return op(t,u); 
        } 
    }; 
     
    #define helperEval(op)     template <class T, class U>  \ 
        decltype((typename T::result() op  typename U::result())) \ 
        operator()(const T& t,const U& u) const \ 
        { \ 
            std::cout<<#op " called " \ 
            <<t()<<" "<<u()<<std::endl;    \ 
            return t() op u(); \ 
        } 
     
    struct plus 
    { 
        helperEval(+) 
    }; 
     
    struct mul 
    { 
         helperEval(*) 
    }; 
     
    struct DIV 
    { 
         helperEval(/) 
    }; 
     
    struct moins 
    { 
         helperEval(-) 
    }; 
     
     
    template <class T, class U> ExpressionBin<T,U,plus> 
     operator+(const T& t,const U& u) 
    { 
        return ExpressionBin<T,U,plus>(t,u); 
    } 
    template <class T, class U> ExpressionBin<T,U,mul> 
    operator*(const T& t,const U& u) 
    { 
        return ExpressionBin<T,U,mul>(t,u); 
    } 
     
    template <class T, class U> ExpressionBin<T,U,DIV> 
    operator/(const T& t,const U& u) 
    { 
        return ExpressionBin<T,U,DIV>(t,u); 
    } 
     
    template <class T, class U> ExpressionBin<T,U,moins> 
    operator-(const T& t,const U& u) 
    { 
        return ExpressionBin<T,U,moins>(t,u); 
    } 
     
    int main(int argc, char *argv[]) 
    { 
        Var<int> x(5); 
        const Var<int> y(3); 
        const Var<double> z(+2.5); 
        Var<double> a; 
        Var<int> b=2; 
        auto e= makeExpression(x*y+z); 
        std::cout<<"========="<<std::endl; 
        a=0.5; 
        std::cout<<"=>"<<e()<<std::endl; 
        std::cout<<"========="<<std::endl; 
        return 0; 
    }
    Pour le moment le code marche (disons qu'il me donne le résultat attendu) mais j'ai une incompréhension

    Si je commente le constructeur par défaut de ExpressionBin, le code ne veut plus compiler car il ne trouve pas le dit constructeur. Jusque là c'est logique ....

    Sauf que ce soucis apparaît seulement quand j'ai plus d'une opération dans mon calcul. si je fais seulement x*y, pas besoin constructeur par défaut. Mais dès que je passe à x*y+z (ou quoi que ce soit d'autre), j'en ai besoin.

    Ce qui me choque, c'est que le constructeur n'est jamais appelé et que les références puissent être laissées non initialisées !! D'ailleurs, si je le remplace par ExpressionBin()=default;
    ca plante !!

    Est ce que quelqu'un aurait une explication à ce phénomène ?

    Merci beaucoup !
    David.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Ça ne viendrait pas du fait que le type de z est différent de celui de x et y ?

    Je ne comprend d'ailleurs pas comment peut s'effectuer l'opération x*y. Je ne vois pas d'opérateur * pour la classe Var. L'opérateur () doit être automatiquement appelé, mais je ne connais pas ce mécanisme.

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Je n'ai pas de problème pour compiler ton code avec g++ 4.7.3.
    Si toutefois je corrige la ligne 12...

  4. #4
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Je n'ai pas tout les éléments de réponse à tes questions. Par contre, en utilisant des std::declval à la place d'utiliser explicitement les constructeurs dans tes typedef (c'est là que tu utilises tes constructeurs par défaut, ce n'est pas une utilisation runtime, mais ça implique que la syntaxe soit valide), ça devrait corriger certains comportements je pense.

  5. #5
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    oodini >> Oui, le code compile et donne le résultat attendu. Sinon j'ai des opérateurs templates pour +, *, / et - (l97 a 110)

    Flob90 >> merci, je n'avais pas vu std::declval. Je suis pas encore totalement au point sur C++11. Et effectivement, ca corrige le comportement.

    Reste plus qu'un bug au niveau logique de l'application. Si je trace mes appels, j'ai
    * called 5 3
    + called 15 2.5
    * called 5 3
    =>17.5
    Le deux premières lignes sont normales mais je en vois pas pourquoi la dernière apparaît ... D'autant plus que le type de e est bon
    Expression< ExpressionBin< ExpressionBin<Var<int>, Var<int>, mul>, Var<double>,plus> >
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par Davidbrcz Voir le message
    oodini >> Oui, le code compile et donne le résultat attendu. Sinon j'ai des opérateurs templates pour +, *, / et - (l97 a 110
    Sur les ExpressionBin, mais pas sur les var.
    Or, quand tu fais x*y, il s'agit de Var.

    Si quelqu'un veut bien m'expliquer la chose...

  7. #7
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @oodini: Les opérateurs de David matchent bien x*y avec T=var<int> et U=var<int>.

    @David: Le fait que le comportement soit différent entre =default et fait à la main doit venir d'un détail de la norme, je regardes plus en détail.

    Pour le std::declval, en réalité c'est la même idée qu'avant le C++11, on se débrouille pour avoir des fonctions qui donne le type qu'on veut sans avoir à réellement les appeler. decltype simplifie la syntaxe mais ne change pas la façon de faire.

  8. #8
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Pour ton "double" affichage, regardes bien ta macro. Tu appelles 2 fois l’évaluation des sous-expressions (u() et t() deux fois). Or le * est une sous-expression, elle est donc affichée deux fois. Enlèves la seconde ligne dans la macro (<< u() << t()) et tu auras bien une seule fois l'affichage.

    PS: Par contre méfies toi avec l'enchainement des <<, ca donne pas forcément les affichages les plus lisibles (au niveau de l'ordre).

  9. #9
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    @oodini: Les opérateurs de David matchent bien x*y avec T=var<int> et U=var<int>.
    Avec ce code :

    Var<int> x(5);
    const Var<int> y(3);
    auto e= makeExpression(x*y);

    Avant de pouvoir appeler make_expression, on doit faire x*y.
    Multiplication entre deux Var<int>. Mais comment cela est-il possible ?

  10. #10
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <class T, class U> ExpressionBin<T,U,mul> 
    operator*(const T& t,const U& u) 
    { 
        return ExpressionBin<T,U,mul>(t,u); 
    }
    Avec T=var<int> et U=var<int> :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ExpressionBin<var<int>,var<int>,mul> 
    operator*(const var<int>& t,const var<int>& u) 
    { 
        return ExpressionBin<var<int>,var<int>,mul>(t,u); 
    }

  11. #11
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Pour ton "double" affichage, regardes bien ta macro. Tu appelles 2 fois l’évaluation des sous-expressions (u() et t() deux fois). Or le * est une sous-expression, elle est donc affichée deux fois. Enlèves la seconde ligne dans la macro (<< u() << t()) et tu auras bien une seule fois l'affichage.
    Arf ><.
    Tellement le nez dans le code que je l'ai pas vu....
    Comme quoi un oeil extérieur c'est toujours bien !

    Bon bah résolu !

    Prochaine étape, avoir une conversion automatique des scalaires en const Var.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  12. #12
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Je ne suis qu'une buse. Désolé de vous avoir fait perdre du temps.

  13. #13
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    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 189
    Points : 17 141
    Points
    17 141
    Par défaut
    mais non, pas du tout, on ne perd pas notre temps.
    Ca nous fait réfléchir et on ré-apprend…

    Ca fait office de piqure de rappel, ce n'est jamais mauvais.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  14. #14
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    cet article date de mathusalem, je le cite dans ma biblio de thèse.
    Y a plein d'approximation fallacieuse sur l'implémentation.


    Oh et boost.proto quoi :o

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

Discussions similaires

  1. comment modifier le constructeur par défaut
    Par une_tite_question dans le forum NetBeans
    Réponses: 6
    Dernier message: 18/06/2008, 21h24
  2. notion de constructeur par défaut
    Par new_wave dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 09/05/2008, 19h33
  3. Réponses: 8
    Dernier message: 27/10/2006, 14h36
  4. Réponses: 11
    Dernier message: 25/08/2006, 16h00
  5. Constructeur par défaut en cas de surcharge
    Par gigi_m dans le forum MFC
    Réponses: 4
    Dernier message: 08/06/2005, 09h58

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