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

Langage C++ Discussion :

[C++] std::for_each et this ?


Sujet :

Langage C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut [C++] std::for_each et this ?
    Bonjour,

    J'ai une petite question de 'foncteur' j'ai à peu près compris à quoi cela pouvis bien servir et je me suis lancé dans un petit essai.

    J'ai écrit ça :

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(this->unVecteur.begin(),this->unVecteur.end(),this);

    Et la c'est le drame, je ne suis pas vraiment dans m'esprit de la FAQ nous sommes bien d'accord, mais j'aimerais comprendre pourquoi cettr formulation est fausse.

    Dans l'esprit j'aurrai voulu fusionner this->unVecteur avec un truc genre this->unAutreVecteur cette fusion étant faite via le foncteur (enfin l'opérateur () )

    Si vous pouviez m'éclairer

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,
    D'après la doc de SGI :
    template <class InputIterator, class UnaryFunction>
    UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f);
    Le troisième argument de for_each doit être une fonction unaire, c'est à dire une fonction à un argument. Tu ne peux pas lui passer this, qui est un pointeur !

    Edit :
    Dans l'esprit j'aurrai voulu fusionner this->unVecteur avec un truc genre this->unAutreVecteur cette fusion étant faite via le foncteur (enfin l'opérateur () )
    Je ne comprends pas trop, pourrais-tu préciser ?

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Je viens de (re)lire ça je n'avais pas saisi le portée de la chose :s
    Je m'étais dit que puisuqe que l'on pouvait faire ce genre de chose

    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
     
     
    struct myclass {
      void operator() (int i) {cout << " " << i;}
    } myobject;
     
    int main () {
      vector<int> myvector;
      myvector.push_back(10);
      myvector.push_back(20);
      myvector.push_back(30);
     
      cout << "\nmyvector contains:";
      for_each (myvector.begin(), myvector.end(), myobject);
     
      cout << endl;
     
      return 0;
    }

    J'aurrai pu mettre le for_each dans myobject et utiliser this pour le troisième argument (je suis aller un peu vite semble t'il :s)

    Ce n'est pas comme ça que cela amarche visiblement, je suis donc passé par une étape supplémentaire en passant en tant que 3eme argument l'opérateur () de ma classe

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        std::for_each(  this->unVecteur.begin(),
                        this->unVecteur.end(),
                       &MaClass::operator () );
    Dans l'idée ça me paraisait mieux, je lui passe un opérateur unaire, j'ai défini ()

    Code .hpp : Sélectionner tout - Visualiser dans une fenêtre à part
    void operator()(int i);
    Code .cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MaClass::operator ()(int i)
    {
    this->m_count+=i;
    }


    J'ai simplifié l'exemple mais j'ai toujours un problèmle de compile que je ne comprends pas

    Code Le compilo : Sélectionner tout - Visualiser dans une fenêtre à part
    c:\program files\microsoft visual studio 8\vc\include\algorithm(29) : error C2064: le terme ne correspond pas à une fonction qui prend 1 arguments

    Alors que pour moi () prends bien un argument et un seul...

    PS: J'ai simplifié mon besoin pour avoir un truc simple et compréhensible ^^

  4. #4
    Membre éclairé Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Points : 845
    Points
    845
    Par défaut
    Par rapport à ton tout premier exemple, tu peux faire ça:

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(this->unVecteur.begin(),this->unVecteur.end(),*this);

    Ou ça:

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(this->unVecteur.begin(),this->unVecteur.end(),std::bind1st(std::mem_fun(&myclass::operator()), this));

    .. normalement

  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
    Regarde ceci et cela. Cela devrait te convenir
    "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 régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Citation Envoyé par metagoto Voir le message
    Par rapport à ton tout premier exemple, tu peux faire ça:

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(this->unVecteur.begin(),this->unVecteur.end(),*this);
    J'y ai pensé hier soir (en bas du batiment) et j'ai eu la flemme de remonter au bureau (en haut du batiment, fait chaud tout ça :p) Cette notation marche et me plait bien. J'ai pas été bien malin en remplacant l'objet de l'example par this

    Par contre j'ai lu les articles sur les algo de la stl et j'ai apri des trucs ^^
    Merci à vous.

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Humm

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
     std::for_each(this->unVecteur.begin(),this->unVecteur.end(),*this);

    1 - Ca compile pas de problème, par contre ça boucle pour une raison qui méchappe :s Si j'ai 2 éléments dans le vecteur, je vois passer dans l'operateur()

    elem0 elem1 elem0 elem1 elem0 elem1 elem0 elem1 ....

    Est-ce bien normal ? Je suppose que oui, mais je ne vois pas pourquoi sa boucle comme cela...


    2 - J'ai aussi regarder du coté de mem_fun en ajoutant une fonction membre à ma classe.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        std::for_each(this->unVecteur.begin(),this->unVecteur.end(),     std::mem_fun(&MaClasse::MaFonction));
    (mon vecteur contien des pointeurs vers des objets, d'ou le mem_fun)

    Avec cela je retombe sur le problème suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error C2064: le terme ne correspond pas à une fonction qui prend 1 arguments
    Il faudrait donc que j'utilise un bind1st ou bind2nd mais je ne comprends pas :
    • - Ou sont mes deux paralmètres (je n'en vois toujours q'un :s)
      - Quel binding utiliser :s


    Lors de l'instanciation du template je me retrouve avec cette erreur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
            with
    1>        [
    1>            _Fn1=std::mem_fun1_t<void,CMaclass,int *>,
    1>            _Ty=int *,
    1>            _Alloc=std::allocator<int *>,
    1>            _Result=void,
    1>            _Arg=int *,
    1>            _InIt=std::_Vector_iterator<int *,std::allocator<int *>>
    1>        ]
    Ce que j'en comprends c'est que le foncteur à une signature à 3 paramètres
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     _Fn1=std::mem_fun1_t<void,CMaclass,int *>
    Et la je dois avouer que je sèche complètement...

  8. #8
    Membre éclairé Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Points : 845
    Points
    845
    Par défaut
    Donc si je comprends bien, tu as un vecteur membre de pointers sur des int

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::for_each(this->unVecteur.begin(),this->unVecteur.end()
                 ,std::mem_fun(&MaClasse::MaFonction));
    Ce code ne peut pas fonctionner car mem_fun cherche à caller MaClasse::MaFonction dans le context des élements du vecteur. Ce sont des int* donc c'est pas bon.

    Il faudrait donc que j'utilise un bind1st ou bind2nd mais je ne comprends pas :
    * - Ou sont mes deux paralmètres (je n'en vois toujours q'un :s)
    - Quel binding utiliser :s
    bind1st et bind2nd ne servent juste qu'à "fixer" un paramètre en première ou 2nd position d'un functor binaire.

    Ce qu'il faut bien comprendre, c'est que les fonctions membres non statiques ont un premier paramètre implicite qui est un pointer sur l'instance de l'objet.
    En Python, par exemple, ce premier paramètre est explicite: "def foo(self, x)"
    En C++, on aurait "void foo(X)" mais en fait le compilo considère "void foo(T* this, X)"

    std::mem_fun (ou mem_fun_ref) retourne un functor qui attend un objet en tant que premier paramètre. Ce premier paramètre est le "this" implicite.

    Pour que ce this implicite soit bindé non pas sur les int* de ton vecteur mais ton instance (this) de ta classe, il faut utiliser un functor special qui va opérer ces manipulations de paramètre: bind1st ou bind2nd.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::bind1st( std::mem_fun(&MaClasse::MaFonction), this)
    ^ Equivalent à &MaClasse::MaFonction(MaClasse* this, Optionel opt)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::bind2nd( std::mem_fun(&MaClasse::MaFonction), this)
    ^ Equivalent à &MaClasse::MaFonction(MaClasse* opt, Optionel this) <-- pas bon

    Ce que j'en comprends c'est que le foncteur à une signature à 3 paramètres
    Ces paramètres sont, dans l'ordre: type de retour, type de l'instance sur laquelle on applique la fonction membre, type du premier paramètre (qui est en fait le 2ième paramètre, le premier étant le this implicite)

    Essai ça, si ça peux t'aider:
    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
    #include <iostream>
    #include <iterator>
    #include <vector>
     
    struct MaClasse {
     
      MaClasse()
      {
        unVecteur.push_back(10);
        unVecteur.push_back(20);
        unVecteur.push_back(30);
      }
     
      void test()
      {
        std::for_each(this->unVecteur.begin(), this->unVecteur.end()
                     ,std::bind1st( std::mem_fun(&MaClasse::MaFonction), this));
     
        // simple back_inserter
        //std::copy(this->unVecteur.begin(), this->unVecteur.end()
        //         ,back_inserter(unAutreVecteur));
     
        // utilise MaClasse::operator()
        //std::for_each(this->unVecteur.begin(), this->unVecteur.end()
        //             ,std::bind1st( std::mem_fun(&MaClasse::operator()), this));
      }
     
      void MaFonction(int i)
      {
        unAutreVecteur.push_back(i);
      }
     
      void operator()(int i)
      {
        unAutreVecteur.push_back(i);
      }
     
      std::vector<int> unVecteur;
      std::vector<int> unAutreVecteur;
    };
     
    int main () {
      MaClasse a;
      a.test();
     
      // juste pour l'output
      std::copy(a.unAutreVecteur.begin(), a.unAutreVecteur.end()
               ,std::ostream_iterator<int>(std::cout, " "));
     
      return 0;
    }

  9. #9
    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
    Il est a noter que boost::bind fournit un moyen bien plus simple (en écriture) pour ce genre ce choses.
    "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)

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    J'ai 'enfin' compri...
    J'ai eu aussi queqlues blagues avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(this->unVecteur.begin(),this->unVecteur.end(),*this);
    Bien sur *this est copié, ce qui est rédibitoire dans mon appli => boucle => crash

    Cette fois, c'est vraiment résolu, merci pour ces éclaircissements

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ceci dit, l'idéal reste toujours d'avoir un foncteur "séparé" (même s'il est imbriqué dans une structure de données)...

    En effet, la solution que tu présente n'aurait, en définitive, un intérêt que si tu envisage le fait que la boucle doive être appelée récursivement...

    Et la récursivité est rarement la meilleure solution
    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

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Ceci dit, l'idéal reste toujours d'avoir un foncteur "séparé" (même s'il est imbriqué dans une structure de données)...

    En effet, la solution que tu présente n'aurait, en définitive, un intérêt que si tu envisage le fait que la boucle doive être appelée récursivement...

    Et la récursivité est rarement la meilleure solution
    C'est un idéal d'ordre 'théorique' pour l lisibilité ou ce genre de chose ou le fait d'utiliser les foncteur comme je l'ai fait (fonction membre d'un objet) à des répercussions plus larges ?

    Dans mon cas, et de mon point de vue, le foncteur est une fonction membre de ma classe car il agit sur une des données membre de la classe en question. Je n'aimais pas l'idée de séparer le foncteur de la classe, j’ai voulu en faire une fonction membre pour que le foncteur soit ‘visiblement’ rattaché à la classe qui l'héberge.

    Aurais-je raté quelque chose ?

  13. #13
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par chronos Voir le message
    C'est un idéal d'ordre 'théorique' pour l lisibilité ou ce genre de chose ou le fait d'utiliser les foncteur comme je l'ai fait (fonction membre d'un objet) à des répercussions plus larges ?
    Non, c'est un idéal d'ordre tout à fait pratique, qui a des répercutions plus large...

    En effet, la récursivité souffre:
    • du fait qu'elle ne peut être que difficilement êtreinlinee
    • du fait que chaque appel récursif va occasionner l'ajout d'informations sur la pile d'appel

    Le premier point est certes le moins important au vu du matériel actuel, mais il fallait le citer

    Le deuxième point ouvre la porte en grand au risque de "stack overflow", surtout dans le cas de double récursivité, et risque d'ébranler la sécurité générale de ton programme

    C'est pourquoi il est toujours recommandé de n'utiliser la récurisvité que lorsqu'elle est réellement indispensable et / ou qu'il n'y a pas de possibilité simple à mettre en oeuvre d'utilisation des boucles "classiques"
    Dans mon cas, et de mon point de vue, le foncteur est une fonction membre de ma classe car il agit sur une des données membre de la classe en question. Je n'aimais pas l'idée de séparer le foncteur de la classe, j’ai voulu en faire une fonction membre pour que le foncteur soit ‘visiblement’ rattaché à la classe qui l'héberge.

    Aurais-je raté quelque chose ?
    Pourquoi ne pas créer un foncteur imbriqué, et le déclarer ami de ta classe, dans ce cas là

    Cela te donnerait un code proche 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
    class MaClass
    {
        public:
            /*...*/
            struct MonFoncteur
            {
                 void operator()(MaClass const & c1)
                 {
                      /*...*/
                 }
            }
            friend class MonFoncteur;
            void foo() /* const */
            {
                std::for_each(unVecteur.begin(), unVecteur.end(),MonFoncteur);
            }
        private:
            /*...*/
    };
    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

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Merci pour ces précision, en effet j'évite au maximum la récursivité et dans ce cas précis le but n'est pas de faire une boucle récursive.

    Je cherhche simplement ou mettre mon foncteur pour qu'il soit 'de facto' partie intégrante de la classe, le frien est une idée effectivement, peut être à mettre en balance avec le namespace anonyme (j'ai jamais bien aimé les amis :p)

    Merci encore pour toutes ces réponses.

  15. #15
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Ceci dit, je t'ai présenté le foncteur comme étant déclaré en public dans mon code, mais, s'il est destiné à n'être appelé qu'au départ de fonctions membres, tu peux tout aussi bien le déclarer dans l'accessibilité privée (voir protected s'il est destiné à être appelé depuis les fonctions membres des classes dérivées)...

    De cette manière, tu peux décider de ne pas l'exposer plus que nécessaire, et arriver à garder une encapsulation forte

    Au passage, l'amitié correctement gérée (comprend : déclarée à bon escient) ne brise pas forcément l'encapsulation et permet même de l'améliorer en de nombreuses occasions
    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. Réponses: 6
    Dernier message: 15/11/2012, 11h53
  2. Réponses: 5
    Dernier message: 13/07/2011, 18h56
  3. lambad expression std::for_each
    Par guillaume07 dans le forum C++
    Réponses: 2
    Dernier message: 31/01/2011, 11h26
  4. Améliorations de std::for_each avec les foncteurs
    Par Invité dans le forum Débuter
    Réponses: 15
    Dernier message: 13/05/2010, 12h46
  5. Réponses: 7
    Dernier message: 19/02/2010, 09h42

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