Comment utiliser std :: foreach avec des paramètres / modification
-
20-08-2019 - |
Question
Je me suis retrouvé en train d'écrire
for(int i=0;i<myvec.size();i++)
myvec[i]->DoWhatever(param);
beaucoup, et j'aimerais compresser cela dans une instruction foreach
, mais je ne suis pas sûr de savoir comment obtenir param
sans passer en super-verbose . J'ai aussi des choses comme
for(int i=0;i<myvec.size();i++)
if(myvec[i]->IsOK())
myvec[i]->DoWhatever(param);
et j'aimerais réécrire ce mec aussi. Des pensées?
Oh, aussi, pour diverses raisons, je ne veux pas utiliser de boost.
La solution
#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)
);
}
Autres conseils
Oh, aussi, pour diverses raisons, je ne veux pas utiliser de boost.
Décision valide, mais probablement la mauvaise. Considérez Boost comme une extension de la STL. C ++ est un langage basé sur une bibliothèque. Si vous ne prenez pas cela en compte, votre code sera qualitativement inférieur.
Bien que std :: for_each
puisse être utilisé ici, l'absence d'expressions lambda en C ++ jusqu'à ce que C ++ 0x rend cela fastidieux. Je préconise d'utiliser Boost.ForEach ! Cela facilite beaucoup :
foreach (yourtype x, yourvec)
if (x.IsOK())
x.Whatever();
Ma solution préférée est généralement d'écrire un foncteur pour faire ce dont j'ai besoin:
struct doWhatever {
doWhatever(const Param& p) p(p) {}
void operator(MyVec v&, Param p) {
v.DoWhatever(param);
}
private:
Param p;
};
Et ensuite la boucle:
std::for_each(myvec.begin(), myvec.end(), doWhatever(param));
En fonction du nombre de variantes que vous avez, cela peut être un peu trop détaillé. Il y a cependant beaucoup d'options pour le faire en ligne. boost :: lambda vous permet de créer la fonction dont vous avez besoin sur le site d’appel. boost :: bind (ou les fonctions de liaison de la bibliothèque standard) vous permet de lier le paramètre param à la fonction, vous n'avez donc pas besoin de le fournir sous forme d'argument à chaque fois.
boost :: lambda est probablement l’approche la plus concise et la plus flexible. J'utilise généralement l'approche plain functor car la syntaxe est plus facile à mémoriser. ;)
Bien, lorsque nous avons des compilateurs qui prennent en charge les expressions lambda C ++ 0x, cela devient simple et peu invasif:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
item->DoWhatever(param);
});
et le deuxième exemple peut ressembler à ceci:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
if(item->IsOK())
myvec[i]->DoWhatever(param);
});
#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 ...
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 utilisez-le ensuite comme ceci:
foreach(element, array){
element->DoSomething(); //or (*element)->DoSomething() if type is already a pointer
}
J'utilise ceci sur un tableau personnalisé, mais cela fonctionne également avec std :: vector.