アルゴリズムソリューション:
std::generate(numbers.begin(), numbers.end(), rand);
範囲ベースのforループソリューション:
for (int& x : numbers) x = rand();
std::generate
C ++ 11で範囲ベースのforループよりも詳細なforループを使用したいのはなぜですか?
最初のバージョン
std::generate(numbers.begin(), numbers.end(), rand);
一連の値を生成することを通知します。
2番目のバージョンでは、読者はそれを自分で理解する必要があります。
タイピングの節約は、ほとんどの場合、読み取り時間で失われるため、最適ではありません。ほとんどのコードは、入力されるよりもはるかに多く読み取られます。
forループが範囲ベースであるかどうかはまったく違いはありませんが、括弧内のコードを単純化するだけです。アルゴリズムは、意図を示すという点でより明確です。
個人的に、私の最初の読書:
std::generate(numbers.begin(), numbers.end(), rand);
は「範囲内のすべてに割り当てています。範囲はnumbers
です。割り当てられる値はランダムです」です。
私の最初の読書:
for (int& x : numbers) x = rand();
「範囲内のすべてに何かを行っています。範囲はnumbers
です。ランダムな値を割り当てることです。」
それらはかなり似ていますが、同一ではありません。私が最初の読みを誘発したいと思うかもしれない1つのもっともらしい理由は、このコードについての最も重要な事実は、それが範囲に割り当てられるということだと思うからです。だからあなたの「なぜ私がしたいのか...」があります。generate
C++ではstd::generate
「範囲の割り当て」を意味するので使用します。ところでstd::copy
、2つの違いは、割り当て元のものです。
ただし、交絡因子があります。範囲ベースのforループにはnumbers
、イテレータベースのアルゴリズムよりも、範囲がであることを本質的に直接表現する方法があります。そのため、人々は範囲ベースのアルゴリズムライブラリに取り組んでいます。バージョンboost::range::generate(numbers, rand);
よりも見栄えがします。std::generate
それに対して、int&
範囲ベースのforループにはしわがあります。範囲の値型がそうでない場合は、コードが要素に割り当て可能からの戻りにのみ依存するのに対し、int
ここでは、変換可能であることに依存する厄介な微妙なことを行っています。値型が、であっても、そうであるかどうかを考えるのをやめるかもしれません。したがって、これは、何が割り当てられるかがわかるまで、型について考えるのを延期します。つまり、 「どの型を持っていても、範囲要素を参照する」と言います。C ++ 03に戻ると、アルゴリズム(関数テンプレートであるため)は正確な型を非表示にする方法でしたが、現在は方法になっています。int&
generate
rand
int
auto
auto &x
最も単純なアルゴリズムは、同等のループよりもわずかな利点しかありません。範囲ベースのforループは、ループを改善します(主にボイラープレートの大部分を削除しますが、それよりも少し多くなります)。そのため、マージンが狭くなり、特定のケースでは気が変わってしまう可能性があります。しかし、そこにはまだスタイルの違いがあります。
私の意見では、Effective STL Item 43:「手書きのループよりもアルゴリズムの呼び出しを優先する」。まだ良いアドバイスです。
begin()
私は通常、 /end()
地獄を取り除くためにラッパー関数を作成します。これを行うと、例は次のようになります。
my_util::generate(numbers, rand);
意図の伝達と読みやすさの両方で、forループに基づく範囲を上回っていると思います。
そうは言っても、C ++ 98では、一部のSTLアルゴリズム呼び出しで発話できないコードが生成され、「手書きループよりもアルゴリズム呼び出しを優先する」に従うのは良い考えではなかったことを認めなければなりません。幸いなことに、ラムダはそれを変えました。
ハーブサッターの次の例を考えてみましょう:Lambdas、LambdasEverywhere。
タスク: vの最初の要素であるとを見つけ> x
ます< y
。
ラムダなし:
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; } );
私の意見では、手動ループは冗長性を減らすかもしれませんが、読みやすく欠けています。
for (int& x : numbers) x = rand();
このループを使用して、数値で定義された範囲を初期化することはしません1。これを見ると、数値の範囲で反復しているように見えますが、実際には(本質的に)そうではありません。範囲からの読み取り、範囲への書き込みです。
を使用すると、意図がはるかに明確になりますstd::generate
。
1.このコンテキストでの初期化とは、コンテナの要素に意味のある値を与えることを意味します。
イテレータを入力として受け取るアルゴリズムでは、範囲ベースのループでは(単純に)実行できないことがいくつかあります。たとえばstd::generate
:
コンテナをlimit
(除外され、limit
上の有効なイテレータですnumbers
)まで、ある分布の変数で満たし、残りを別の分布の変数で満たします。
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 ::アルゴリズムは特定のコンテナーとタイプに特化している可能性があり、クリーンな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について話している場合は、いいえ、アルゴリズムは範囲ベースので廃止されることはないと主張します。C ++標準委員会には、C ++に範囲を追加する提案に取り組んでいる研究グループがあり、多形ラムダで行われている作業もあります。範囲を使用すると、イテレータのペアを使用する必要がなくなり、多形ラムダを使用すると、ラムダの正確な引数タイプを指定できなくなります。これは、このstd::for_each
ように使用できることを意味します(これを難しい事実と見なさないでください。今日の夢は、まさにそのように見えます)。
std::for_each(c.range(), [](x)
{
// do stuff with x
});
注意すべきことの1つは、アルゴリズムがどのように行われるかではなく、何が行われるかを表現することです。
範囲ベースのループには、物事が行われる方法が含まれます。最初の要素から開始し、適用して、最後まで次の要素に進みます。単純なアルゴリズムでさえ、異なることを行う可能性があり(少なくとも特定のコンテナーのオーバーロード、恐ろしいベクトルについてさえ考えていない)、少なくともそれが行われる方法はライターの仕事ではありません。
私にとって、それは大きな違いであり、可能な限りカプセル化し、可能な場合はアルゴリズムを使用して文を正当化します。
範囲ベースのforループはまさにそれです。もちろん基準が変更されるまで。
アルゴリズムは関数です。パラメータにいくつかの要件を課す関数。要件は、たとえば、使用可能なすべての実行スレッドを利用して自動的に高速化する実装を可能にするために、標準で表現されています。