可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've found myself writing
for(int i=0;i<myvec.size();i++)
myvec[i]->DoWhatever(param);
a lot, and I'd like to compress this into a foreach
statement, but I'm not sure how to get param
in there without going super-verbose. I've also got things like
for(int i=0;i<myvec.size();i++)
if(myvec[i]->IsOK())
myvec[i]->DoWhatever(param);
and I'd like to rewrite that guy too. Any thoughts?
Oh, also, for various reasons, I don't want to use boost.
回答1:
#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)
);
}
回答2:
Oh, also, for various reasons, I don't want to use boost.
Valid decision, but most likely the wrong one. Consider Boost as an extension to the STL. C++ is a library-driven language. If you don't take this into account, your code will be qualitatively inferior.
While std::for_each
can be used here, the absence of lambda expressions in C++ until C++0x makes this tedious. I advocate using Boost.ForEach! It makes this much easier:
foreach (yourtype x, yourvec)
if (x.IsOK())
x.Whatever();
回答3:
My preferred solution is usually to write a functor to do what I need:
struct doWhatever {
doWhatever(const Param& p) p(p) {}
void operator(MyVec v&, Param p) {
v.DoWhatever(param);
}
private:
Param p;
};
And then the loop:
std::for_each(myvec.begin(), myvec.end(), doWhatever(param));
Depending on how many variations of this you have, this might be a bit too verbose.
There are plenty of options for doing it inline though.
boost::lambda would let you construct the function you need at the call-site. boost::bind (or the standard library bind functions) would let you bind the parameter param to the function so you don't need to supply it as an argument every time.
boost::lambda is probably the most concise and flexible approach. I usually use the plain functor approach because the syntax is easier to remember. ;)
回答4:
well when we have compilers that support C++0x lambda expresions, this becomes straightforward and minimally invasive:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
item->DoWhatever(param);
});
and the second example may look like this:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
if(item->IsOK())
myvec[i]->DoWhatever(param);
});
回答5:
#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)
)
);
}
You can simplify it by using namespaces...
回答6:
If you are using GCC you can define something like:
#define foreach(element, array) \
for(typeof((array).begin()) element = (array).begin(), __end_##element = (array).end();\
element != __end_##element;\
++element)
and use it after like this:
foreach(element, array){
element->DoSomething(); //or (*element)->DoSomething() if type is already a pointer
}
I use this on a custom array but it works fine with std::vector too.