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 templates, approche forcement vecteurs?


Sujet :

C++

  1. #1
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut expression templates, approche forcement vecteurs?
    Bonjour,

    j'ai l'impression, après avoir lu le très instructif article sur la métaprog, que la seule façon d'utiliser les expressions templates, c'est en ayant une approche basée sur les vecteurs et les itérateurs.

    Quelle est la bonne approche lorsque les données d'un objet à additionner (ou autre opération) sont différentes ?

    Pour une classe Point2D ayant comme membres m_x et m_y, il suffit de faire m_coord[2], mais que se passe-t-il lorsqu'on fait l'addition de deux objets qui ont un short et un std::string par exemple ?

  2. #2
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    Tu utilises les expressions templates sur ce que tu veux.

    C'est par exemple un des moyen d'écrire un concaténeur qui à partir de toutes les chaines reçues en argument (std::string, char*, ...) détermine sur une première passe la longueur requise pour le résultat et termine par véritablement concaténer toutes les chaines.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Ce n'est pas vraiment ce que j'ai voulu dire.

    un exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Inutile
    {
      int m_x;
      std::string m_chaine;
     
      operator = (Inutile& i)
      {
        m_x += i.m_x;
        m_chaine += i.m_chaine;
      }
    }
    J'ai du mal a voir comment implémenter une expression template de cette classe qui doit additionner des types différents.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Salut,

    Pour autant que l'opérateur + soit défini pour chacun des types passés en template, cela devrait ressembler à quelque chose du genre 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
    17
    18
    19
    20
    21
     
    template <typename T, typename U>
    class Inutile
    {
        private:
            T _first;
            U _second;
        public:
            Inutile(const T& t, const U& u):_first(t),_second(u){}
            ~Inutile(){}
            Inutile& operator+(Inutile& two)
            {
                 _first+=two._first;
                 _second+=two._second;
                 return *this;
            }
            void Affiche()
            {
                std::cout<<_first<<" "<<_second;
            }
    };
    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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Pour te montrer que cela fonctionne, si tu commente les lignes qui ont trait à troisieme, quatrieme et res2, la compilation du code suivant
    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
     
    int main()
    {
        Inutile<std::string, int> premier("salut ",2);
        Inutile<std::string, int> deuxieme("tout le monde",3);
        Inutile<std::string, int> res=premier+deuxieme;
        res.Affiche();
        /* les lignes qui suivent ne fonctionneront pas, étant donné que les 
         * types ne corresondent pas 
         */
        Inutile<int, std::string> troisieme(2,"salut ");
        Inutile<std::string, int> quatrieme("tout le monde",3);
        Inutile<std::string, int> res2=troisieme+quatrieme;
        res2.Affiche();
     
        return 0;
    }
    (pour autant que tu aies inclus <string> et <iostream> )
    te fournira la sortie écran:
    salut tout le monde 5
    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 éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Merci pour ta réponse mais celà ne résoud toujours pas mon problème.

    Je parle bien ici d'expressions templates en métaprogrammation!

    Deux remarques à ta solution :
    l'opérateur + ne devrait pas modifier this. C = A + B ne doit pas modifier A
    Une implémentation correcte de l'operateur + renvoie un objet temporaire, créé pour l'occasion et de fait, n'est pas optimisée.

    Les expressions templates en métaprogrammation permettent de conserver une écriture lisible ( D = A+B*C ) mais sont optimisées car elles ne passent pas par des objets intermédiaires, elles font les calculs sur les variables membres directement. Le compilateur et les templates sont les clefs du système.

    Cela marche bien lorsqu'on a un tableau de type unique comme une classe Vecteur ou Point, Rectangle, car l'opérateur ++ d'incrémentation est utilisé. Mais lorsque les types sont différents, l'exemple donné dans le tutoriel semble atteindre ses limites et je n'ai pas assez d'expérience en métaprog pour trouver l'astuce.

    Peut-être en plaçant la déclaration des variables de la classe de façon a ce qu'elle aient des adresses contigues et en faisant des réinterpret_cast ?

  7. #7
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Dans l'exemple du vecteur, on utilise une boucle pour parcourir tous les éléments car ils ont tous le même type. Mais si tu dois gérer des variables de types différents cela ne change rien, il te suffit de fournir les accesseurs sous une autre forme.

    Par exemple, au lieu de fournir un seul opérateur () et un opérateur ++, tu peux fournir deux opérateurs () : un qui agit sur le membre int et un autre qui agit sur le membre std::string.

  8. #8
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    La meilleure façon d'utiliser les expression templates c'est sûrement avec boost.proto.
    http://boost-sandbox.sourceforge.net...roto/doc/html/

  9. #9
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Citation Envoyé par Laurent Gomila
    Dans l'exemple du vecteur, on utilise une boucle pour parcourir tous les éléments car ils ont tous le même type. Mais si tu dois gérer des variables de types différents cela ne change rien, il te suffit de fournir les accesseurs sous une autre forme.

    Par exemple, au lieu de fournir un seul opérateur () et un opérateur ++, tu peux fournir deux opérateurs () : un qui agit sur le membre int et un autre qui agit sur le membre std::string.
    Donc c'est chaque BinaryOp et UnaryOp qu'il faut revoir, et pas seulement l'operateur = de la classe Inutile, c'est dommage.

    Mon idée de placer les données à des adresses qui se suivent et de faire des ++ puis des reinterpret_cast ne serait-elle pas à exploiter ?

  10. #10
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Deuxieme question,

    Dans le tutoriel, je ne comprends pas trop pourquoi, dans la classe Vector, l'operateur * (Vector&, double) a cette signature.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <class T>BinaryOp<T, UnaryOp<double, Const>, Mult> operator *(const T& v1, double d)
    {
        return MakeMult(v1, MakeConst(d));
    }
    BinaryOp<Vector::const_iterator, UnaryOp<double, Const>, Mult> operator *(const Vector& v1, double d)
    {
        return MakeMult(v1.begin(), MakeConst(d));
    }
    Pourquoi doit on passer par MakeConst ? N'etait-il pas possible de laisser deux références vers les objets dans la signature ? pourquoi l'opérateur + a-t-il été implémenté comme celà lui ?

    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 <class T, class U> BinaryOp<T, U, Add> operator +(const T& v1, const U& v2)
    {
        return MakeAdd(v1, v2);
    }
    template <class T> BinaryOp<Vector::const_iterator, T, Add> operator +(const Vector& v1, const T& v2)
    {
        return MakeAdd(v1.begin(), v2);
    }
    template <class T> BinaryOp<T, Vector::const_iterator, Add> operator +(const T& v1, const Vector& v2)
    {
        return MakeAdd(v1, v2.begin());
    }
    BinaryOp<Vector::const_iterator, Vector::const_iterator, Add> operator +(const Vector& v1, const Vector& v2)
    {
        return MakeAdd(v1.begin(), v2.begin());
    }

  11. #11
    Rédacteur
    Avatar de Bakura
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    1 386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 386
    Par défaut
    Si j'ai bien compris ta question, tu te demandes pourquoi c'est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template <class T>BinaryOp<T, UnaryOp<double, Const>, Mult> operator *(const T& v1, double d)
    {
        return MakeMult(v1, MakeConst(d));
    }
    et non :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <class T, class U>BinaryOp<T, U>, Mult> operator *(const T& v1, double d)
    {
        return MakeMult(v1, d);
    }
    .

    Tout d'abord, je vais commencer à répondre à la seconde question, sur la différence avec l'operateur +. Le MakeConst c'est pour les constantes (1.0, 2.0, 3.8...).

    Ici le tuto sur les et implémentent la possibilité de faire expression template * scalaire, ou vecteur * scalaire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // Permet d'écrire expression template * scalaire
    template <class T>BinaryOp<T, UnaryOp<double, Const>, Mult> operator *(const T& v1, double d)
    {
        return MakeMult(v1, MakeConst(d));
    }
    // Permet d'écrire vecteur * scalaire
    BinaryOp<Vector::const_iterator, UnaryOp<double, Const>, Mult> operator *(const Vector& v1, double d)
    {
        return MakeMult(v1.begin(), MakeConst(d));
    }
    Alors que dans la surcharge de l'opérateur + (regarde les commentaires que j'ai ajouté) :

    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
     
    // Permet expression template + expression template
    template <class T, class U> BinaryOp<T, U, Add> operator +(const T& v1, const U& v2)
    {
        return MakeAdd(v1, v2);
    }
    // Permet vecteur + expression tempalte
    template <class T> BinaryOp<Vector::const_iterator, T, Add> operator +(const Vector& v1, const T& v2)
    {
        return MakeAdd(v1.begin(), v2);
    }
    // Permet expression template + vecteur
    template <class T> BinaryOp<T, Vector::const_iterator, Add> operator +(const T& v1, const Vector& v2)
    {
        return MakeAdd(v1, v2.begin());
    }
    // Permet vecteur + vecteur
    BinaryOp<Vector::const_iterator, Vector::const_iterator, Add> operator +(const Vector& v1, const Vector& v2)
    {
        return MakeAdd(v1.begin(), v2.begin());
    }
    Comme tu peux le voir, pour l'opérateur + il n'y a pas de surcharge vecteur + scalaire ou expression template + scalaire, d'où le fait que tu ne voyes pas de MakeConst.

    Pour la première question :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    BinaryOp<Vector::const_iterator, UnaryOp<double, Const>, Mult> operator *(const Vector& v1, double d)
    {
        return MakeMult(v1.begin(), MakeConst(d));
    }
    Le MakeConst a comme effet ici de créer un objet de type UnaryOp <double, Const>. En effet, si tu laisse le double, tu vas avoir un problème lors de l'évaluation de l'expression.

    Imagine que tu fasses : v1 = v2 * 2.0;

    v2 * 2.0 va créer une expression template de type :
    BinaryOp <Vector::const_iterator, UnaryOp <double, Const>, Mult> (voir la surcharge de l'opérateur au dessus).

    Ensuite, la surcharge de l'opérateut d'égalité va être appelé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <class T> const Vector& Vector::operator =(T Expr)
    {
        for (iterator i = begin(); i != end(); ++i, ++Expr)
            *i = *Expr;
     
        return *this;
    }
    Ou T équivaudra à ce que j'ai mis juste au dessus, c'est à dire :
    BinaryOp <Vector::const_iterator, UnaryOp <double, Const>, Mult>

    Et là, tu vas vite comprendre pourquoi ON DOIT créer un objet UnaryOp plutôt que de laisser un double tout seul.

    Je te rappelle que, dans le tuto, l'opérateur *() dans la classe BinaryOp est surchargée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    inline double operator *() {return *It1 * *It2;}
    Et là, le problème se voit tout de suite : en créant un objet UnaryOp <double, Const> pour le double, on a la possibilité de surchargé l'opérateur *(), qui est, pour UnaryOp pour les constantes :

    inline double operator *() {return It;}

    Et qui renvoit donc toujours le double.

    Alors que si tu aurais laissé un double sans créer d'objet, tu aurais eu forcément un soucis (tu aurais eu en gros *It1 * *unDouble).

    Puis à la deuxième passe de la boucle for de la surcharge de ==, Expr sera incrémenté (++Expr) :

    inline void operator ++() {++It1; ++It2;}

    Là encore, en ayant It2 un objet de type UnaryOp <double, Const>, on peut surchargé l'opérateur ++, qui dans le cas d'un double, ben ne fais rien :

    inline void operator ++() {}

    Alors que si tu aurais laissé un double, ben ça t'aurais incrémenté de 1 ton double.



    Je sais pas si j'ai été très clair, j'espère ne pas avoir dit de connerie, mais sinon ce n'est pas ma présentation préférée pour les expressions templates et sur les tests que j'avais fait, l'héritage plombait totalement les perfs.

  12. #12
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Si c'est très clair sur l'utilisation de Const.

    C'est bien que tu soulèves la question car j'allais la poser :

    ok un arbre est construit et les constructeurs d'objets temporaires sont évités, mais on va en fait créer de nombreux objets Unary et BinaryOp donc les gains de perfs sont-ils rééls ?

  13. #13
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Si tout est inliné, les objets temporaires ne seront même pas construits, tout sera éliminé à la compilation. C'est le but, sinon évidemment on ne mettrait pas en place de telles techniques

  14. #14
    Rédacteur
    Avatar de Bakura
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    1 386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 386
    Par défaut
    Si c'est très clair sur l'utilisation de Const.
    Cool alors .

    C'est bien que tu soulèves la question car j'allais la poser :

    ok un arbre est construit et les constructeurs d'objets temporaires sont évités, mais on va en fait créer de nombreux objets Unary et BinaryOp donc les gains de perfs sont-ils rééls ?
    Pour tout te dire, j'ai récemment codé une classe vecteur en utilisant les expressions templates. En fait, en regardant un profiler, on s'apperçoit que sans aucune optimisation, tout se fait à la compilation, c'est à dire que je pouvais voir la création d'expressions très très complexes, dans le cas d'un v1 = v2 + v3 + v4 * 2.0 par exemple ça donne un joli Add <Add <Add <Mult <T, Scalar>, T>, T, T> > ou quelque chose comme ça, enfin un vrai bordel, et là ça a l'air plus pénalisant qu'autre chose.

    Par contre, avec les optimisations maximum activées, là le profiler devient vide, ce qui prouve que tout est éliminé à la compilation.

    EDIT : oui et comme le dit Loulou, il faut que tout soit inliné .

    Concernant un quelconque avantage niveau performance par rapport à une méthode "normale", ben ce n'est pas si flagrant. C'est pour ça que là j'ai codé des classes mathématiques normalement, et qu'après quand j'aurai de vrais applications je retesterai les ET voir si ça m'apporte quelque chose, mais pour le moment ça compliquerai juste tout pour vraiment peu de chose. Et puis sur pas mal de fonctions, produit scalaire, produit vectoriel, ça n'apporte strictement rien.

    Pour la méthode utilisée sur le site, je te conseille de virer l'héritage, perso sur mes tests, l'héritage plombait pas mal tout. Donc au lieu d'avoir ça :

    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
    // Modèle de classe pour nos opérateurs binaires
    template <class T, class U, int Op> struct BinaryOp {};
     
    // Classe de base pour nos opérateurs binaires
    template <class T, class U> struct BinaryOpBase
    {
        BinaryOpBase(T I1, U I2) : It1(I1), It2(I2) {}
        inline void operator ++() {++It1; ++It2;}
     
        T It1;
        U It2;
    };
     
    // Spécialisation pour l'addition
    template <class T, class U> struct BinaryOp<T, U, Add> : public BinaryOpBase<T, U>
    {
        BinaryOp(T I1, U I2) : BinaryOpBase<T, U>(I1, I2) {}
        inline double operator *() {return *It1 + *It2;}
    };
    Tu auras ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Modèle de classe pour nos opérateurs binaires
    template <class T, class U, int Op> struct BinaryOp {};
     
    // Spécialisation pour l'addition
    template <class T, class U> struct BinaryOp<T, U, Add>
    {
        BinaryOp(T I1, U I2) : a (I1), b(I2) {}
        inline void operator ++() {++a; ++b;}
        inline double operator *() {return *It1 + *It2;}
     
        private:
           const T a;
           const T b;
    };

  15. #15
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    C'est étrange que l'heritage "plombe" le systeme. Si il n'y a pas de methodes virtuelles le compilo devrait pouvoir retrouver ses billes et générer les mêmes raccourcis non ?

    Enfin merci pour vos réponses, je suis en train de tester sur des classes bidons. Je viendrai donner les résultats.

  16. #16
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Citation Envoyé par NiamorH
    C'est étrange que l'heritage "plombe" le systeme. Si il n'y a pas de methodes virtuelles le compilo devrait pouvoir retrouver ses billes et générer les mêmes raccourcis non ?
    Le compilateur doit pouvoir tout supprimer, avec de l'héritage, il peut avoir plus de mal.

    Perso, je n'ai pas exactement fait comme Laurent, ce que je sauvegarde, ce sont des références constantes et je travaille au niveau des objets, non pas au niveau itérateurs qui ne rentrent en jeu qu'au moment de l'affectation.

Discussions similaires

  1. expression template et constructeur par défaut
    Par Davidbrcz dans le forum C++
    Réponses: 13
    Dernier message: 16/06/2013, 23h14
  2. Conflit avec expressions templates
    Par Nanoc dans le forum C++
    Réponses: 5
    Dernier message: 29/01/2010, 20h23
  3. Réponses: 6
    Dernier message: 24/04/2009, 15h05
  4. Problème expression templates
    Par Bakura dans le forum C++
    Réponses: 5
    Dernier message: 06/06/2007, 20h08
  5. Réponses: 12
    Dernier message: 27/01/2007, 12h32

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