web-dev-qa-db-ja.com

std :: for_eachを使用する必要がありますか?

私は常に、使用する言語(さまざまなスタイル、フレームワーク、パターンなど)について詳しく学びたいと思っています。 _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 )。

43
Alan Turing

古い学校のforループの代わりにstd::for_eachを使用することには利点があります(または新しいC++ 0x範囲-forループも):最初のWordを見ることができますステートメントのあなたは、あなたがステートメントが何をするかを正確に知っています。

for_eachを見ると、ラムダ内の操作が範囲内の各要素に対して1回だけ実行されていることがわかります(例外がスローされないと想定)。すべての要素が処理される前の早い段階でループから抜け出すことはできません。また、要素をスキップしたり、1つの要素のループの本体を複数回評価したりすることもできません。

forループでは、ループの本体全体を読み取って、ループの機能を知る必要があります。制御フローを変更するcontinuebreak、またはreturnステートメントが含まれている場合があります。イテレータまたはインデックス変数を変更するステートメントがある場合があります。ループ全体を調べなければ、知る方法はありません。

Herb Sutterは、アルゴリズムとラムダ式を使用する利点について説明しました Northwest C++ Users Groupへの最近のプレゼンテーションで

必要に応じて、実際にstd::copyアルゴリズムをここで使用できることに注意してください。

std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
46
James McNellis

場合によります。

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){
  // ...
});

ラムダのパラメータリストのTmy_type<int>::nested_typeのような冗長なタイプになる可能性があるため、「同等」はわずかに主観的です。しかし、彼/彼女の方法でtypedefすることができます。正直なところ、ラムダが型の演繹で多態性を持つことが許可されなかった理由はまだわかりません...


ここでもう1つ検討する必要があるのは、名前自体であるfor_eachがすでに意図を表現していることです。これは、シーケンス内の要素はスキップされないことを示しています。これは、通常のforループの場合と同じです。

それは私に別のポイントをもたらします:for_eachはシーケンス全体を実行し、コンテナのeveryアイテムに操作を適用することを目的としているため、初期のreturnsまたはbreaks全般。 continueは、ラムダ/ファンクターからのreturnステートメントでシミュレートできます。

したがって、for_eachを使用します本当にコレクション内のeveryアイテムに操作を適用したい場合。

余談ですが、for_eachは、範囲ベースの素晴らしいforループ(foreachループとも呼ばれます)のおかげで、C++ 0xでは単に「非推奨」になる可能性があります。

for(auto& item : container){
  // ...
}

これはかなり短く(イェイ)、次の3つのオプションすべてを許可します。

  • 早期に戻る(戻り値があっても!)
  • ループから抜け出し、
  • 一部の要素をスキップします。
25
Xeo

通常、std::for_eachの使用をお勧めします。 forループの例は、非ランダムアクセスコンテナーでは機能しません。イテレータを使用して同じループを記述できますが、反復変数のタイプとしてstd::SomeContainerName<SomeReallyLongUserType>::const_iteratorを書き出すため、通常は面倒です。 std::for_eachはこれからあなたを隔離し、endへの呼び出しを自動的に償却します。

9
Billy ONeal

私見、あなたはあなたのテストコードでこの新機能を試すべきです。

製品コードでは、使いやすい機能を試してみてください。 (つまり、for_each、それを使用できます。)

8
iammilind

for_eachは、シーケンスを反復する最も一般的なアルゴリズムであるため、表現力が最も低くなります。反復の目標をtransformaccumulatecopyで表すことができる場合、汎用のfor_eachではなく、特定のアルゴリズムを使用する方が良いと思います。

(gcc 4.6.0でサポートされています。ぜひお試しください)の新しいC++ 0x範囲を使用すると、for_eachは、関数をシーケンスに適用する最も一般的な方法であるニッチを失うことさえあります。

3
Cubbi

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, " " ) );
0
BЈовић

Boost.Rangeは、標準アルゴリズムの使用を簡素化します。あなたの例では、あなたは書くことができます:

boost::for_each(v, [](int n) { cout << n << endl; });

(またはboost::copy他の回答で提案されているようにostreamイテレータを使用)。

0
rafak

「従来の」例はバグがあることに注意してください。

_for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
_

これは、intが常にベクター内のすべての値のインデックスを表すことができることを前提としています。これがうまくいかない場合、実際には2つの方法があります。

1つは、intが_std::vector<T>::size_type_よりもランクが低い可能性があることです。 32ビットマシンでは、intsは通常32ビット幅ですが、v.size()はほぼ確実に64ビット幅になります。 2 ^ 32要素をベクターに詰め込むと、インデックスが最後まで到達しません。

2番目の問題は、符号付きの値(int)を符号なしの値(_std::vector<T>::size_type_)と比較していることです。したがって、それらが同じランクであったとしても、サイズが最大整数値を超えると、インデックスがオーバーフローし、未定義の動作がトリガーされます。

このベクトルの場合の場合、これらのエラー条件が真になることはありません。ただし、コンパイラの警告を無視するか無効にする必要があります。また、それらを無効にすると、コードの他の場所で実際のバグを見つけるのに役立つ警告のメリットが得られません。 (コードがそれらを有効にすることを実行可能にした場合、これらのコンパイラ警告によって検出されるべきバグを追跡するのに多くの時間を費やしました。)

したがって、そうです、_for_each_(または適切な_<algorithm>_)は、intsのこの有害な悪用を回避するため、より優れています。また、範囲ベースのforループまたは反復子ベースのループをautoで使用することもできます。

インデックスではなく_<algorithm>_ sまたはイテレータを使用することの追加の利点は、それを使用するすべてのコードをリファクタリングすることなく、将来コンテナタイプを変更する柔軟性が高まることです。

0
Adrian McCarthy