私は常に、使用する言語(さまざまなスタイル、フレームワーク、パターンなど)について詳しく学びたいと思っています。 _std::for_each
_ は絶対に使用しないことに気づいたので、おそらく始めるべきだと思いました。そのような場合の目標は、心を拡大することであり、ではなくコードをある程度改善することです(読みやすさ、表現力、コンパクトさなど)。
したがって、そのコンテキストを念頭に置いて、たとえばベクトルの出力などの単純なタスクには_std::for_each
_を使用することをお勧めします。
_for_each(v.begin(), v.end(), [](int n) { cout << n << endl; }
_
([](int n)
はラムダ関数です)。の代わりに:
_for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
_
この質問が無意味に思えないことを願っています。 今回で実際に必要としない場合でも、中間のプログラマーが言語機能を使用する必要がある場合、それはほとんどより大きな質問をするようです...ちょうど彼が実際にそれから大いに利益を得るかもしれない時間の間機能をよりよく理解することができるように。このより大きな質問はおそらくすでに尋ねられていますが(例えば here )。
古い学校のfor
ループの代わりにstd::for_each
を使用することには利点があります(または新しいC++ 0x範囲-for
ループも):最初のWordを見ることができますステートメントのあなたは、あなたがステートメントが何をするかを正確に知っています。
for_each
を見ると、ラムダ内の操作が範囲内の各要素に対して1回だけ実行されていることがわかります(例外がスローされないと想定)。すべての要素が処理される前の早い段階でループから抜け出すことはできません。また、要素をスキップしたり、1つの要素のループの本体を複数回評価したりすることもできません。
for
ループでは、ループの本体全体を読み取って、ループの機能を知る必要があります。制御フローを変更するcontinue
、break
、またはreturn
ステートメントが含まれている場合があります。イテレータまたはインデックス変数を変更するステートメントがある場合があります。ループ全体を調べなければ、知る方法はありません。
Herb Sutterは、アルゴリズムとラムダ式を使用する利点について説明しました Northwest C++ Users Groupへの最近のプレゼンテーションで 。
必要に応じて、実際にstd::copy
アルゴリズムをここで使用できることに注意してください。
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
場合によります。
for_each
の威力は、イテレータが入力イテレータの概念を満たしているコンテナであればどのコンテナでも使用できることです。そのため、あらゆるコンテナで一般的に使用できます。これにより、コンテナーを交換するだけで、何も変更する必要がないように、保守性が向上します。同じことは、ベクトルのsize
のループにも当てはまりません。ループを変更せずにスワップできる他の唯一のコンテナーは、別のランダムアクセスコンテナーです。
ここで、イテレータのバージョンを自分で入力すると、通常のバージョンは次のようになります。
// substitute 'container' with a container of your choice
for(std::container<T>::iterator it = c.begin(); it != c.end(); ++it){
// ....
}
どちらかと言えば長いですよね? C++ 0xは、auto
キーワードを使用して、長さの問題から解放されます。
for(auto it = c.begin(); it != c.end(); ++it){
// ....
}
もういいですが、まだ完璧ではありません。すべての反復でend
を呼び出しており、それをよりよく行うことができます。
for(auto it = c.begin(), ite = c.end(); it != ite; ++it){
// ....
}
よさそうだ。それでも、同等のfor_each
バージョンより長い:
std::for_each(c.begin(), c.end(), [&](T& item){
// ...
});
ラムダのパラメータリストのT
はmy_type<int>::nested_type
のような冗長なタイプになる可能性があるため、「同等」はわずかに主観的です。しかし、彼/彼女の方法でtypedef
することができます。正直なところ、ラムダが型の演繹で多態性を持つことが許可されなかった理由はまだわかりません...
ここでもう1つ検討する必要があるのは、名前自体であるfor_each
がすでに意図を表現していることです。これは、シーケンス内の要素はスキップされないことを示しています。これは、通常のforループの場合と同じです。
それは私に別のポイントをもたらします:for_each
はシーケンス全体を実行し、コンテナのeveryアイテムに操作を適用することを目的としているため、初期のreturn
sまたはbreak
s全般。 continue
は、ラムダ/ファンクターからのreturn
ステートメントでシミュレートできます。
したがって、for_each
を使用します本当にコレクション内のeveryアイテムに操作を適用したい場合。
余談ですが、for_each
は、範囲ベースの素晴らしいforループ(foreachループとも呼ばれます)のおかげで、C++ 0xでは単に「非推奨」になる可能性があります。
for(auto& item : container){
// ...
}
これはかなり短く(イェイ)、次の3つのオプションすべてを許可します。
通常、std::for_each
の使用をお勧めします。 forループの例は、非ランダムアクセスコンテナーでは機能しません。イテレータを使用して同じループを記述できますが、反復変数のタイプとしてstd::SomeContainerName<SomeReallyLongUserType>::const_iterator
を書き出すため、通常は面倒です。 std::for_each
はこれからあなたを隔離し、end
への呼び出しを自動的に償却します。
私見、あなたはあなたのテストコードでこの新機能を試すべきです。
製品コードでは、使いやすい機能を試してみてください。 (つまり、for_each
、それを使用できます。)
for_each
は、シーケンスを反復する最も一般的なアルゴリズムであるため、表現力が最も低くなります。反復の目標をtransform
、accumulate
、copy
で表すことができる場合、汎用のfor_each
ではなく、特定のアルゴリズムを使用する方が良いと思います。
(gcc 4.6.0でサポートされています。ぜひお試しください)の新しいC++ 0x範囲を使用すると、for_each
は、関数をシーケンスに適用する最も一般的な方法であるニッチを失うことさえあります。
for
ループスコープC++ 11を使用できます
例えば:
T arr[5];
for (T & x : arr) //use reference if you want write data
{
//something stuff...
}
Tは必要なすべてのタイプです。
これは、STLおよびクラシック配列のすべてのコンテナーで機能します。
まあ...それはうまくいきますが、ベクトル(または他のコンテナタイプのコンテンツ)を印刷するために私はこれを好みます:
std::copy(v.begin(), v.end(), std::ostream_iterator< int >( std::cout, " " ) );
Boost.Rangeは、標準アルゴリズムの使用を簡素化します。あなたの例では、あなたは書くことができます:
boost::for_each(v, [](int n) { cout << n << endl; });
(またはboost::copy
他の回答で提案されているようにostreamイテレータを使用)。
「従来の」例はバグがあることに注意してください。
_for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
_
これは、int
が常にベクター内のすべての値のインデックスを表すことができることを前提としています。これがうまくいかない場合、実際には2つの方法があります。
1つは、int
が_std::vector<T>::size_type
_よりもランクが低い可能性があることです。 32ビットマシンでは、int
sは通常32ビット幅ですが、v.size()
はほぼ確実に64ビット幅になります。 2 ^ 32要素をベクターに詰め込むと、インデックスが最後まで到達しません。
2番目の問題は、符号付きの値(int
)を符号なしの値(_std::vector<T>::size_type
_)と比較していることです。したがって、それらが同じランクであったとしても、サイズが最大整数値を超えると、インデックスがオーバーフローし、未定義の動作がトリガーされます。
このベクトルの場合の場合、これらのエラー条件が真になることはありません。ただし、コンパイラの警告を無視するか無効にする必要があります。また、それらを無効にすると、コードの他の場所で実際のバグを見つけるのに役立つ警告のメリットが得られません。 (コードがそれらを有効にすることを実行可能にした場合、これらのコンパイラ警告によって検出されるべきバグを追跡するのに多くの時間を費やしました。)
したがって、そうです、_for_each
_(または適切な_<algorithm>
_)は、int
sのこの有害な悪用を回避するため、より優れています。また、範囲ベースのforループまたは反復子ベースのループをautoで使用することもできます。
インデックスではなく_<algorithm>
_ sまたはイテレータを使用することの追加の利点は、それを使用するすべてのコードをリファクタリングすることなく、将来コンテナタイプを変更する柔軟性が高まることです。