アルゴリズムソリューション:
std::generate(numbers.begin(), numbers.end(), Rand);
範囲ベースのforループソリューション:
for (int& x : numbers) x = Rand();
なぜもっと冗長なstd::generate
C++ 11の範囲ベースのforループ上?
最初のバージョン
std::generate(numbers.begin(), numbers.end(), Rand);
一連の値を生成することを伝えます。
2番目のバージョンでは、読者は自分でそれを理解する必要があります。
タイピングの節約は、ほとんどの場合、読み取り時間で失われるため、最適ではありません。ほとんどのコードは、入力されるよりも多く読み込まれます。
Forループが範囲ベースであるかどうかにかかわらず、違いはありませんが、括弧内のコードが単純化されるだけです。アルゴリズムはintentを示すという点でより明確です。
個人的に、私の最初の読書:
_std::generate(numbers.begin(), numbers.end(), Rand);
_
「範囲内のすべてに割り当てています。範囲はnumbers
です。割り当てられた値はランダムです」です。
私の最初の読み:
_for (int& x : numbers) x = Rand();
_
「範囲内のすべてに対して何かを行っています。範囲はnumbers
です。ランダムな値を割り当てるだけです。」
それらはかなり似ていますが、同一ではありません。私が最初のリーディングを挑発したいと思う可能性のある理由の1つは、このコードの最も重要な事実は、コードが範囲に割り当てられるということです。それで、あなたが「なぜ私がしたいのか...」があります。 C++では_std::generate
_は「範囲割り当て」を意味するため、generate
を使用します。 btwが_std::copy
_を行うように、2つの違いは割り当て元です。
ただし、交絡要因があります。範囲ベースのforループには、反復子ベースのアルゴリズムよりも、範囲がnumbers
であることを本質的に直接表現する方法があります。そのため、範囲ベースのアルゴリズムライブラリに取り組んでいます。boost::range::generate(numbers, Rand);
は、_std::generate
_バージョンよりも見栄えがします。
それに対して、範囲ベースのforループの_int&
_はしわです。範囲の値の型がint
ではない場合、_generate
コードのみであるのに対し、_int&
_に変換できるかどうかに依存する厄介な微妙な処理をここで行っています。要素に割り当て可能なRand
からの戻り値に依存します。値の型がint
であっても、それがそうであるかどうかを考えるのをやめるかもしれません。したがって、auto
は、何が割り当てられるかを見るまで、型について考えるのを遅らせます-_auto &x
_を使用して、「範囲要素への参照を取得します。 C++ 03に戻ると、アルゴリズムは(関数テンプレートであるため)正確な型を非表示にするthe方法でしたが、現在はa方法です。
私は、最も単純なアルゴリズムが同等のループに比べてほんのわずかな利点しかないということは常に当てはまると思います。範囲ベースのforループは、ループを改善します(主にボイラープレートのほとんどを削除することにより、それより少しだけ多くなります)。そのため、マージンが厳しくなり、特定のケースではおそらく考えが変わります。しかし、まだスタイルの違いがあります。
私の意見では、効果的なSTL項目43:「手書きのループへのアルゴリズム呼び出しを優先する」。まだ良いアドバイスです。
私は通常、begin()
/end()
hellを取り除くためのラッパー関数を作成します。これを行うと、例は次のようになります。
my_util::generate(numbers, Rand);
意図の伝達と読みやすさの両方で、ループベースのforループに勝ると思います。
そうは言っても、C++ 98では一部のSTLアルゴリズム呼び出しが発話できないコードを生成し、「手書きのループへのアルゴリズム呼び出しを優先する」のは良い考えのように思われなかったことを認めなければなりません。幸い、ラムダはそれを変えました。
Herb Sutter:Lambdas、Lambdas Everywhere の次の例を考えてみましょう。
タスク:> x
および< y
であるvの最初の要素を検索します。
ラムダなし:
auto i = find_if( v.begin(), v.end(),
bind( logical_and<bool>(),
bind(greater<int>(), _1, x),
bind(less<int>(), _1, y) ) );
ラムダ付き
auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } );
myの意見では、手動ループは冗長性を低下させる可能性がありますが、読みやすくはありません:
for (int& x : numbers) x = Rand();
初期化にこのループを使用しません1 numbersで定義された範囲。これを見ると、それは反復の範囲であるように見えますが、実際には(本質的に) )、つまり、範囲からreadingの代わりに、範囲に対してwritingです。
std::generate
を使用すると、意図がより明確になります。
1.このコンテキストでのinitializeは、コンテナの要素に意味のある値を与えることを意味します。
入力としてイテレータを使用するアルゴリズムが可能な範囲ベースのループでは、(単純に)実行できないことがいくつかあります。たとえば、std::generate
:
コンテナーをlimit
まで(除外します。limit
はnumbers
の有効なイテレーターです)、1つの分布からの変数と残りを別の分布からの変数で埋めます。
std::generate(numbers.begin(), limit, Rand1);
std::generate(limit, numbers.end(), Rand2);
イテレータベースのアルゴリズムにより、操作している範囲をより適切に制御できます。
std::generate
の特定のケースでは、読みやすさ/意図の問題に関する以前の回答に同意します。 std :: generateは私にはより明確なバージョンのようです。しかし、これはある意味で好みの問題だと認めます。
とはいえ、私にはstd :: algorithmを捨てない理由がもう1つあります。一部のデータ型に特化した特定のアルゴリズムがあります。
最も単純な例はstd::fill
です。一般バージョンは、提供された範囲のforループとして実装され、このバージョンはテンプレートをインスタンス化するときに使用されます。しかしいつもではない。例えば。 std::vector<int>
の範囲を指定すると、実際には内部でmemset
が呼び出され、はるかに高速で優れたコードが生成されます。
ここで効率化カードをプレイしようとしています。
手書きのループはstd :: algorithmバージョンと同じくらい高速かもしれませんが、ほとんど高速にはなりません。それだけでなく、std :: algorithmは特定のコンテナーとタイプに特化していて、クリーンなSTLインターフェースの下で行われます。
私の答えは多分そうです。 C++ 11について話している場合は、多分(いいえのように)です。例えば std::for_each
はラムダでさえも使用するのが本当に面倒です:
std::for_each(c.begin(), c.end(), [&](ExactTypeOfContainedValue& x)
{
// do stuff with x
});
ただし、範囲ベースのforを使用する方がはるかに優れています。
for (auto& x : c)
{
// do stuff with x
}
一方、C++ 1yの場合は、いいえ、アルゴリズムはforに基づく範囲によって廃止されることはないと主張します。 C++標準委員会には、C++に範囲を追加するための提案に取り組んでいる研究グループがあり、また、多形ラムダに対して行われている作業もあります。範囲を指定すると、イテレータのペアを使用する必要がなくなり、多相ラムダを使用すると、ラムダの引数の型を正確に指定できなくなります。この意味は std::for_each
は次のように使用できます(これを難しい事実と見なさないでください。これは、今日の夢の外観です):
std::for_each(c.range(), [](x)
{
// do stuff with x
});
範囲ベースのforループはそれだけです。もちろん規格が変わるまで。
アルゴリズムは関数です。パラメータにいくつかの要件を課す関数。要件は、使用可能なすべての実行スレッドを利用して、自動的にスピードアップする実装例を可能にするために、標準で表現されています。
注意すべきことの1つは、アルゴリズムは、どのようにではなく、何が行われるかを表すということです。
範囲ベースのループには、最初の要素から始めて、最後の要素まで適用して次の要素に移動するという方法が含まれます。単純なアルゴリズムでさえ別のことを行うことができ(少なくとも特定のコンテナーの過負荷で、恐ろしいベクトルについては考えていません)、少なくともそれが行われる方法はライタービジネスではありません。
私にとってそれは違いの大部分であり、可能な限りカプセル化し、可能な場合は文を正当化し、アルゴリズムを使用します。