パラメータ/変更でstd :: foreachを使用する方法
-
20-08-2019 - |
質問
自分が書いていることに気付いた
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);
そして私もその男を書き直したいと思います。何か考えはありますか?
また、さまざまな理由から、ブーストを使用したくありません。
解決
#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)
);
}
他のヒント
また、さまざまな理由で、ブーストを使用したくありません。
有効な決定ですが、おそらく間違った決定です。 BoostをSTLの拡張として検討してください。 C ++はライブラリ駆動型言語です。これを考慮しない場合、コードは質的に劣ります。
ここではstd::for_each
を使用できますが、C ++ 0xでラムダ式が存在しないため、C ++ 0xを使用するのは面倒です。 Boost.ForEach を使用して提唱しています。これにより、ずっと簡単になります:
foreach (yourtype x, yourvec)
if (x.IsOK())
x.Whatever();
私が好む解決策は、通常、必要なことを行うファンクターを書くことです:
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を使用すると、呼び出しサイトで必要な関数を作成できます。 boost :: bind(または標準ライブラリバインド関数)を使用すると、パラメータparamを関数にバインドできるため、毎回引数として指定する必要がありません。
boost :: lambdaは、おそらく最も簡潔で柔軟なアプローチです。構文は覚えやすいので、通常は単純なファンクターアプローチを使用します。 ;)
C ++ 0xラムダ式をサポートするコンパイラがある場合、これは簡単で低侵襲になります:
std::for_each(myvec.begin(),myvec.end(),[&](X& item){
item->DoWhatever(param);
});
2番目の例は次のようになります:
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)
)
);
}
名前空間を使用して単純化できます...
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でも問題なく動作します。