これはコアの問題 728であり、ジェネリック ラムダが存在する前に提出されました。
あなたはジェネリックラムダについて言及し、それらは対応する member を持つローカルクラスと同一であると述べましたtemplate operator()
。ただし、実際にはそうではなく、違いは実装の特性に関連しています。検討
template <typename T>
class X {
template <typename>
void foo() {
T t;
}
};
と
template <typename T>
auto bar() {
return [] (auto) {T t;};
};
これらのテンプレートのインスタンス<void>
化は、最初のケースでは問題ありませんが、2 番目のケースでは形式が正しくありません。最初のケースでなぜうまくいくのですか?foo
特定の ごとにインスタンス化可能である必要はありませんがT
、そのうちの 1 つだけです (これは[temp.res]/(8.1)になります)。
2 番目のケースでは、なぜ形式が正しくないのでしょうか。ジェネリック ラムダの本体は、提供されたテンプレート引数を使用して (部分的に) インスタンス化されます。そして、この部分的なインスタンス化の理由は…
…関数定義の処理中に使用されるレキシカルスコープは基本的に一時的なものです。つまり、関数テンプレート定義の一部のインスタンス化を遅らせることはサポートしにくいということです。
( Richard Smith ) ローカルの「テンプレート」を十分にインスタンス化して、ローカル コンテキスト (それを囲む関数テンプレートのテンプレート パラメーターを含む) から独立させる必要があります。
これは、 [expr.prim.lambda]/13の理論的根拠にも関連しています
。これは、次の場合にエンティティがラムダによって暗黙的にキャプチャされることを義務付けています…</p>
評価される可能性のある式 ([basic.def.odr]) でエンティティに名前を付けます。ここで、囲んでいる完全な式は、 lambda-expressionの到達範囲内で宣言された汎用ラムダ パラメーターに依存します。
つまり、 のようなラムダがある場合[=] (auto x) {return (typename decltype(x)::type)a;}
、のメンバー typedef が forであるかどうかa
に関係なく、キャストは のキャプチャを引き起こします。ラムダの呼び出し用。この問題の議論については、ジェネリック ラムダに関する元の提案を参照してください。x
void
a
肝心なのは、メンバー テンプレートのインスタンス化を完全に延期することは、(少なくとも 1 つの) 主要な実装で使用されるモデルと互換性がなく、それらが予期されるセマンティクスであるため、機能が導入されなかったということです。
それがこの制約の最初の動機でしたか? これは 1994 年 1 月から 5 月の間に導入されましたが、それをカバーする論文はありませんでした。そのため、ローカル クラスがテンプレート引数であってはならない理由に関するこの論文の正当化から、一般的な概念の大まかなアイデアを得ることができます。
クラス テンプレートとテンプレートから生成されたクラスは、グローバル スコープのエンティティであり、ローカル スコープのエンティティを参照できません。
おそらく当時、KISSしたかったのでしょう。