Как использовать std:: foreach с параметрами/модификацией
Я обнаружил, что пишу
for(int i=0;i<myvec.size();i++)
myvec[i]->DoWhatever(param);
много, и я хотел бы сжать это в оператор foreach
, но я не уверен, как получить param
туда, не переходя в супер-многословную. У меня также есть такие вещи, как
for(int i=0;i<myvec.size();i++)
if(myvec[i]->IsOK())
myvec[i]->DoWhatever(param);
и я тоже хотел бы переписать этого парня. Любые мысли?
О, также, по разным причинам, я не хочу использовать 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
О, также, по разным причинам, я не хочу использовать boost.
Действительное решение, но, скорее всего, неправильное. Рассмотрим Boost как расширение STL. С++ - это язык, управляемый библиотекой. Если вы не учтете это, ваш код будет качественно хуже.
В то время как std::for_each
можно использовать здесь, отсутствие лямбда-выражений в С++ до тех пор, пока С++ 0x не сделает это утомительным. Я рекомендую использовать Boost.ForEach! Это делает это намного проще:
foreach (yourtype x, yourvec)
if (x.IsOK())
x.Whatever();
Ответ 3
Мое предпочтительное решение - это, как правило, написать функтор, чтобы делать то, что мне нужно:
struct doWhatever {
doWhatever(const Param& p) p(p) {}
void operator(MyVec v&, Param p) {
v.DoWhatever(param);
}
private:
Param p;
};
И затем цикл:
std::for_each(myvec.begin(), myvec.end(), doWhatever(param));
В зависимости от того, сколько вариантов этого вы имеете, это может быть слишком многословным.
Тем не менее, есть много вариантов для этого.
boost:: lambda позволит вам создать нужную функцию на сайте call-сайта. boost:: bind (или стандартные функции связывания библиотек) позволит вам привязать параметр param к функции, поэтому вам не нужно каждый раз передавать его как аргумент.
boost:: lambda - это, пожалуй, самый сжатый и гибкий подход. Обычно я использую простой функтор, потому что синтаксис легче запомнить.;)
Ответ 4
Хорошо, когда у нас есть компиляторы, которые поддерживают выражения С++ 0x lambda, это становится простым и минимально-инвазивным:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
item->DoWhatever(param);
});
а второй пример может выглядеть так:
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)
)
);
}
Вы можете упростить его с помощью пространств имен...
Ответ 6
Если вы используете GCC, вы можете определить что-то вроде:
#define foreach(element, array) \
for(typeof((array).begin()) element = (array).begin(), __end_##element = (array).end();\
element != __end_##element;\
++element)
и использовать его после следующего:
foreach(element, array){
element->DoSomething(); //or (*element)->DoSomething() if type is already a pointer
}
Я использую это в настраиваемом массиве, но он отлично работает с std::vector.