C ++ 0xに収量が追加されなかったのはなぜですか?
質問
私は私のPythonプログラムの多くで利回りを使用しています、そしてそれは 本当 多くの場合、コードをクリアします。私 それについてブログを書いた そして、それは私のサイトで人気のあるページの1つです。
C#は利回りも提供します - 発信者側での状態維持を介して実装され、状態、関数のローカル変数などを維持する自動生成クラスを通じて行われます。
私は現在、C ++ 0xとその追加について読んでいます。また、C ++ 0xでのLambdasの実装について読んでいる間、Lambdaコードを保存するOperator()を装備した自動的に生成されたクラスを介して行われたことがわかりました。私の心の中で形成された自然な質問:彼らはラムダスのためにそれをしました、なぜ彼らは「利回り」のサポートのためにそれを考慮しなかったのですか?
確かに彼らは共同ルーチンの価値を見ることができます...だから私は彼らがマクロベースの実装を考えていると推測することしかできません( サイモン・タタム)適切な代替品として。しかし、それらは多くの理由ではありません:Callee-Copt State、非レントラント、マクロベース(それだけで十分な理由)など。
編集: yield
ゴミ収集、糸、または繊維に依存しません。サイモンの記事を読んで、私が次のような単純な変革を行っているコンパイラについて話していることを確認できます。
int fibonacci() {
int a = 0, b = 1;
while (true) {
yield a;
int c = a + b;
a = b;
b = c;
}
}
の中へ:
struct GeneratedFibonacci {
int state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
state = 1;
while (true) {
return a;
case 1:
int c = a + b;
a = b;
b = c;
}
}
}
}
ゴミコレクション?いいえ。スレッド?いいえ。繊維?いいえ。簡単な変換ですか?おそらく、はい。
解決
彼らはラムダスのためにそれをしました、なぜ彼らも利回りをサポートするためにそれを考慮しなかったのですか?
を確認します 論文. 。誰かがそれを提案しましたか?
...私は彼らがマクロベースの実装を適切な代替品であると考えていると推測することしかできません。
必ずしも。彼らはそのようなマクロソリューションが存在することを知っていると確信していますが、それらを置き換えることは、新しい機能を渡すための十分な動機ではありません。
新しいキーワードにはさまざまな問題がありますが、それらは、Lambdasのために行われ、Autoを関数リターンタイプとして使用するなど、新しい構文で克服することができます。
根本的に新機能には、委員会を通して機能を完全に分析およびプッシュするための強力なドライバー(つまり、人々)には、根本的な変化に懐疑的な多くの人々が常にいるからです。したがって、あなたが利回りの構造に対する強い技術的理由と見なすものを欠いていたとしても、まだ十分なサポートがなかったかもしれません。
しかし、基本的に、C ++標準ライブラリは、収量で見られるものとは異なる反復剤の概念を受け入れています。 2つの操作のみが必要なPythonのイテレーターと比較してください。
- an_iter.next()次のアイテムを返したり、停止を上げたりします(メソッドを使用する代わりに2.6に含まれる[次()ビルトイン)
- iter(an_iter)はan_iterを返します(したがって、関数でiterablesとiteratorsを同じように扱うことができます)
C ++の反復器はペア(同じタイプでなければならない)で使用され、カテゴリに分割され、収量構造に適したものへの移行への意味的なシフトであり、そのシフトは概念にうまく適合しません(ドロップされて以来、それは比較的遅れました)。たとえば、を参照してください 根拠 というのも、(残念なことに)ループの範囲ベースの変更に関する私のコメントをフォームに拒否し、この異なる形式のイテレーターを簡単に書くことができます。
さまざまなイテレータフォームについて意味することを具体的に明確にするために:生成されたコードの例のニーズ 別 タイプのタイプには、これらのイテレーターを取得および維持するためのイテレータータイプと関連する機械になります。処理できなかったわけではありませんが、最初は想像するほど簡単ではありません。真の複雑さは、「ローカル」変数(建設中を含む)の例外を尊重する「単純な変換」、ジェネレーター内のローカルスコープの「ローカル」変数の寿命を制御することです(ほとんどは通話で保存する必要があります)などです。
他のヒント
なぜ彼らがこのようなものを追加しなかったのかは言えませんが、ラムダスの場合、彼らはそうではありませんでした ただ 言語にも追加されました。
彼らはブーストの図書館の実装として人生を始めました、それはそれを証明しました
- ラムダは広く有用です:多くの人が利用可能なときにそれらを使用するでしょう、そしてそれは
これに基づいて、委員会は採用することを決定しました いくつかの種類 c ++ 0xのラムダのof、そして彼らは最初により一般的な言語機能を追加するために実験したと思います より良い Boostが持っているよりもライブラリの実装。
そして最終的に、彼らは他の選択肢がなかったので、彼らはそれをコア言語機能にしました: 十分です 図書館の実装。
新しいコア言語機能は、言語に追加されるだけではありません。委員会はです 非常に それらを追加することを嫌がり、問題の機能 本当 それ自体を証明する必要があります。機能は次のとおりであることを示す必要があります。
- コンパイラに実装することができる、
- 本当のニーズを解決するつもりです
- ライブラリの実装では十分ではないこと。
場合の場合a yield
キーワード、最初のポイントを解決できることがわかります。あなたが示したように、それは機械的に行うことができるかなり単純な変換です。
2番目のポイントはトリッキーです。どれだけ 必要 これはありますか?図書館の実装はどの程度広く使用されていますか?これを求めた人は何人ですか、それとも提案を提出しましたか?
最後のポイントも通過しているようです。少なくともC ++ 03では、あなたが指摘したように、ライブラリの実装がいくつかの欠陥に苦しんでいます。 たぶん......だろう コア言語の実装を正当化します。 C ++ 0xでより良いライブラリの実装を行うことはできますか?
ですから、主な問題は本当に関心の欠如だと思います。 C ++はすでに巨大な言語であり、追加されている機能が追加されない限り、誰もそれを大きくすることを望んでいません 本当 価値がある。これは十分に有用ではないと思います。
以前の有効なコードを無効にするため、キーワードを追加することは常に注意が必要です。 C ++と同じくらい大きいコードベースを持つ言語でそれを避けようとします。
C ++の進化は公共プロセスです。あなたが感じたら yield
そこにある必要があり、C ++標準委員会への適切な要求を策定します。
あなたは、決定を下した人々から直接あなたの答えを得るでしょう。
したがって、C ++ 11またはC ++ 14にはならなかったように見えますが、C ++ 17に向かっている可能性があります。講義を見てください C ++ Coroutines、負のオーバーヘッド抽象化 CPPCON2015と論文から ここ.
要約すると、彼らは機能の特徴として降伏し、待ち望んでいるC ++関数を拡張するように取り組んでいます。 Visual Studio 2015で初期実装があるように見えますが、Clangにまだ実装があるかどうかはわかりません。また、それらは、キーワードとして利回りを使用して待つことの問題であるかもしれません。
プレゼンテーションは興味深いものです。なぜなら、彼はそれがどのくらい単純化されたネットワーキングコードをどれだけ簡素化したかについて話しているからです。驚くべきことに、これらの新しいコルーチンを使用すると、今日のコードよりも速い/少ないコードが得られるように見えます。素晴らしいプレゼンテーションです。
C ++の再開可能な機能提案が見つかります ここ.
一般に、あなたは何が起こっているのかを追跡できます 委員会の論文, 、特定の問題を調べるよりも、追跡する方が良いです。
C ++委員会について覚えておくべきことの1つは、それがボランティア委員会であり、望むすべてを達成できないということです。たとえば、元の標準にはハッシュタイプのマップはありませんでした。委員会に誰も十分に気にかけていなかったかもしれません yield
そして、それが仕事が完了したことを確認するために何をするか。
見つけるための最良の方法は、アクティブな委員会のメンバーに尋ねることです。
まあ、そのような些細な例として、私が見る唯一の問題はそれです std::type_info::hash_code()
指定されていません constexpr
. 。適合の実装は、それを引き続き支持し、これをサポートする可能性があると思います。とにかく、本当の問題は一意の識別子を取得することです。そのため、別の解決策があるかもしれません。 (明らかに、私はあなたの「マスタースイッチ」コンストラクトを借りました、ありがとう。)
#define YIELD(X) do { \
constexpr size_t local_state = typeid([](){}).hash_code(); \
return (X); state = local_state; case local_state: ; } \
while (0)
使用法:
struct GeneratedFibonacci {
size_t state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
while (true) {
YIELD( a );
int c = a + b;
a = b;
b = c;
}
}
}
}
うーん、彼らはまた、ハッシュが0でないことを保証する必要があります。そしてa DONE
マクロは簡単に実装できます。
本当の問題は、ローカルオブジェクトを使用してスコープから戻ると何が起こるかです。 Cベースの言語でスタックフレームを節約する希望はありません。解決策は、実際のコルーチンを使用することであり、C ++ 0xはスレッドと先物で直接対処します。
このジェネレーター/コルーチンを考えてみましょう。
void ReadWords() {
ifstream f( "input.txt" );
while ( f ) {
string s;
f >> s;
yield s;
}
}
同様のトリックが使用されている場合 yield
, f
最初に破壊されます yield
, 、そして、あなたができないので、それの後にループを続けることは違法です goto
また switch
非ポッドオブジェクト定義を過ぎて。
ユーザー空間ライブラリとしてのコルーチンのいくつかの実装がありました。ただし、ここに取引があります。これらの実装は、標準以外の詳細に依存しています。たとえば、C ++標準のどこにも、スタックフレームの維持方法が指定されていません。ほとんどの実装はスタックをコピーするだけです。それがほとんどのC ++実装の動作であるため
標準に関して、C ++は、スタックフレームの仕様を改善することにより、コルーチンのサポートを支援できた可能性があります。
実際に言語にそれを「追加」することは私には良い考えではありません。なぜなら、それは完全にコンパイラに依存するほとんどのケースで「十分な」実装をあなたに貼り付けるからです。コルーチンを使用する場合、これはとにかく受け入れられません
最初に@potatoswatterに同意します。
サポートすることは、コルーチンがラムダのサポートと同じではなく、それではありません 単純な変換 Duffのデバイスで遊んでいるように。
あなたが必要です 完全な非対称コルーチン (スタックフル)Pythonの発電機のように機能する。の実装 サイモン・タタム と クリス どちらもスタックレスです boost.coroutine 重いですが、スタックフルです。
残念ながら、C ++ 11はまだありません yield
まだCoroutinesの場合、おそらくC ++ 1y;)
PS:Pythonスタイルの発電機が本当に好きなら、 これ.