2009-01-24 7 views
20

Je me suis trouvé à écrireComment utiliser std :: foreach avec des paramètres/modification

for(int i=0;i<myvec.size();i++) 
    myvec[i]->DoWhatever(param); 

beaucoup, et j'aimerais Compress dans une déclaration foreach, mais je ne suis pas sûr de savoir comment pour obtenir param là sans aller super-verbeux. J'ai aussi des choses comme

for(int i=0;i<myvec.size();i++) 
    if(myvec[i]->IsOK()) 
     myvec[i]->DoWhatever(param); 

et je voudrais réécrire ce type aussi. Des pensées?

Oh, aussi, pour diverses raisons, je ne veux pas utiliser boost.

Répondre

15
#include <vector> 
#include <algorithm> 
#include <functional> 

class X 
{ 
    public: 
     void doWhat(int x) {} 
     bool IsOK() const {return true;} 
}; 
class CallWhatIfOk 
{ 
    public: 
     CallWhatIfOk(int p): param(p) {} 

     void operator()(X& x) const 
     { if (x.IsOK()) {x.doWhat(param);}} 
    private: 
     int param; 
}; 

int main() 
{ 
    std::vector<X>  myVec; 

    std::for_each( myVec.begin(), 
        myVec.end(), 
        std::bind2nd(std::mem_fun_ref(&X::doWhat),4) 
       ); 


    std::for_each( myVec.begin(), 
        myVec.end(), 
        CallWhatIfOk(4) 
       ); 
} 
+0

Merci! Des idées sur la seconde, cependant? –

7

Oh, aussi, pour diverses raisons, je ne veux pas utiliser boost.

Une décision valide, mais probablement la mauvaise. Considérez Boost comme une extension de la STL. C++ est un langage géré par une bibliothèque. Si vous n'en tenez pas compte, votre code sera qualitativement inférieur.

Alors que std::for_each peut être utilisé ici, l'absence d'expressions lambda en C++ jusqu'à ce que C++ 0x rende cela fastidieux. Je préconise d'utiliser Boost.ForEach! Il rend ce beaucoup plus facile:

foreach (yourtype x, yourvec) 
    if (x.IsOK()) 
     x.Whatever(); 
+0

Hélas, ce n'est pas ma décision d'utiliser boost. –

+8

Peut-être que vous voulez dire que vous * ne pouvez pas * utiliser boost alors. Dire que vous ne * voulez * pas juste demander un argument ...;) – jalf

+0

@jalf Qu'en est-il de * ne sera pas *? –

4

Ma solution préférée est habituellement d'écrire un foncteur faire ce que je dois:

struct doWhatever { 
    doWhatever(const Param& p) p(p) {} 
    void operator(MyVec v&, Param p) { 
    v.DoWhatever(param); 
    } 

private: 
    Param p; 
}; 

Et puis la boucle:

std::for_each(myvec.begin(), myvec.end(), doWhatever(param)); 

Selon sur le nombre de variations de ce que vous avez, cela pourrait être un peu trop verbeux. Il existe cependant de nombreuses options pour le faire en ligne. boost :: lambda vous permet de construire la fonction dont vous avez besoin sur le site d'appel. boost :: bind (ou les fonctions de liaison de bibliothèque standard) vous permet de lier le paramètre param à la fonction de sorte que vous n'avez pas besoin de le fournir comme argument à chaque fois.

boost :: lambda est probablement l'approche la plus concise et la plus flexible. J'utilise généralement l'approche du foncteur simple car la syntaxe est plus facile à retenir. ;)

+0

C'est ce que j'avais (et j'espérais éviter) :) –

+0

hm, sans boost (aïe), je ne pense pas que vous pouvez le faire beaucoup plus court. Dans certains cas, les trucs std :: bind_ * pourraient vous aider, mais il n'y a pas de balles d'argent miraculeuses. (Au moins jusqu'à C++ 0x avec l'ajout d'expressions lambda) – jalf

3

bien quand nous avons compilateurs qui prennent en charge C++ 0x lambda expresions, cela devient simple et peu invasive:

std::for_each(myvec.begin(),myvec.end(),[&](X& item){ 
    item->DoWhatever(param); 
}); 

et le second exemple peut ressembler à ceci:

std::for_each(myvec.begin(),myvec.end(),[&](X& item){ 
    if(item->IsOK())  
     myvec[i]->DoWhatever(param); 
}); 
+0

C'est tellement dommage, ça ne marche pas sur g ++ :(. J'espère que dans un certain temps je serai capable d'utiliser ces constructions ... – Arman

+0

Je l'espère aussi :) – Rick

3
#include <vector> 
#include <algorithm> 
#include <boost/bind.hpp> 
#include <boost/lambda/if.hpp> 
#include <boost/lambda/bind.hpp> 


struct A 
{ 
    bool IsOK() { return true; } 
    void DoWhatever (int param) {} 
}; 

struct B 
{ 
    bool IsOk (A * a) { return true; } 
    void DoWhatever (A * a, int param) {} 
}; 

typedef std::vector<A *> Myvec; 

void main() 
{ 
    Myvec myvec; 
    int param = 1; 
    B b; 

    // first challenge using boost::bind (fnct in the same class) 
    std::for_each (myvec.begin(), myvec.end(), 
    boost::bind (&A::DoWhatever, _1, param)); 

    // first challenge using boost::bind (fnct in an external class) 
    std::for_each (myvec.begin(), myvec.end(), 
    boost::bind (&B::DoWhatever, &b, _1, param)); 

    // second challange using boost::lambda (fnct in the same class) 
    std::for_each (myvec.begin(), myvec.end(), 
    boost::lambda::if_then(
     boost::lambda::bind (&A::IsOK, boost::lambda::_1), 
     boost::lambda::bind (&A::DoWhatever, boost::lambda::_1, param) 
    ) 
); 

    // second challange using boost::lambda (fnct in an external class) 
    std::for_each (myvec.begin(), myvec.end(), 
    boost::lambda::if_then(
     boost::lambda::bind (&B::IsOK, &b, boost::lambda::_1), 
     boost::lambda::bind (&B::DoWhatever, &b, boost::lambda::_1, param) 
    ) 
); 

} 

Vous pouvez le simplifier en utilisant des espaces de noms ...

0

Si vous utilisez GCC, vous pouvez définir quelque chose comme:

#define foreach(element, array) \ 
    for(typeof((array).begin()) element = (array).begin(), __end_##element = (array).end();\ 
     element != __end_##element;\ 
     ++element) 

et de l'utiliser après comme ceci:

foreach(element, array){ 
    element->DoSomething(); //or (*element)->DoSomething() if type is already a pointer 
} 

Je l'utiliser sur un tableau personnalisé, mais il fonctionne très bien avec std: : vecteur aussi.

Questions connexes