Como usar std :: foreach com os parâmetros / modificação
-
20-08-2019 - |
Pergunta
Eu encontrei-me escrevendo
for(int i=0;i<myvec.size();i++)
myvec[i]->DoWhatever(param);
muito, e eu gostaria de compactar isso em uma declaração foreach
, mas eu não tenho certeza como obter param
lá sem ir super-detalhado. Eu também tenho coisas como
for(int i=0;i<myvec.size();i++)
if(myvec[i]->IsOK())
myvec[i]->DoWhatever(param);
e eu gostaria de reescrever esse cara também. Quaisquer pensamentos?
Oh, também, por várias razões, eu não quero usar impulso.
Solução
#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)
);
}
Outras dicas
Oh, também, por várias razões, eu não quero usar impulso.
decisão válida, mas muito provavelmente a pessoa errada. Considere impulso como uma extensão para o STL. C ++ é uma linguagem orientada por biblioteca. Se você não levar isso em conta, o seu código será qualitativamente inferior.
Enquanto std::for_each
podem ser usados ??aqui, a ausência de expressões lambda em C ++ até C ++ 0x torna este tedioso. Eu defendo usando Boost.ForEach ! Faz esta muito mais fácil:
foreach (yourtype x, yourvec)
if (x.IsOK())
x.Whatever();
A minha solução preferida é geralmente para escrever um functor de fazer o que eu preciso:
struct doWhatever {
doWhatever(const Param& p) p(p) {}
void operator(MyVec v&, Param p) {
v.DoWhatever(param);
}
private:
Param p;
};
E então o loop:
std::for_each(myvec.begin(), myvec.end(), doWhatever(param));
Dependendo de quantas variações deste você tem, isso pode ser um pouco muito detalhado. Há uma abundância de opções para fazê-lo em linha embora. boost :: lambda iria deixá-lo construir a função que você precisa na chamada local. boost :: bind (ou a biblioteca padrão ligam funções) iria deixá-lo vincular o parâmetro param para a função, assim você não precisa fornecer isso como um argumento de cada vez.
boost :: lambda é provavelmente o mais conciso e abordagem flexível. Eu costumo usar a abordagem functor simples porque a sintaxe é mais fácil de lembrar. ;)
bem quando temos compiladores que suporte C ++ 0x expresions lambda, este torna-se simples e minimamente invasivo:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
item->DoWhatever(param);
});
e o segundo exemplo pode ser parecido com isto:
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)
)
);
}
Você pode simplificar-lo usando namespaces ...
Se você estiver usando GCC você pode definir algo como:
#define foreach(element, array) \
for(typeof((array).begin()) element = (array).begin(), __end_##element = (array).end();\
element != __end_##element;\
++element)
e usá-lo depois como este:
foreach(element, array){
element->DoSomething(); //or (*element)->DoSomething() if type is already a pointer
}
Eu uso isso em uma matriz costume, mas ele funciona bem com std :: vector também.