34

私が理解している限り、ジェネリックラムダは、テンプレート化されたローカルスコープ構造体のオブジェクトに変換されますoperator()。これにより、汎用ラムダが非常に強力で使いやすいツールになります。一方、構造体にテンプレート化されたメンバーがある場合は、関数にネストされた構造体を作成できます。

#include <iostream>

int main() {
    struct inner {
    template <class T>
       void operator()(T &&i) { }
    };
    return 0;
}

またはそれ自体がテンプレート化されています:

int main() {
    template <class T>
    struct inner {
       void operator()(T &&i) { }
    };
    return 0;
}

コンパイラのコンパイルに問題があるようです:

error: invalid declaration of member template in local class

error: a template declaration cannot appear at block scope

問題は、コンパイラのバグよりも C++ 標準にあると思います。ラムダがローカル構造ではなく、テンプレート化されたメンバーを持つことが許可されている理由は何ですか?

私はこの質問を見つけましたが、答えは時代遅れだと思います(c ++ 11でも真実ではないと思います)。

4

2 に答える 2

28

これはコアの問題 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に関係なく、キャストは のキャプチャを引き起こします。ラムダの呼び出し用。この問題の議論については、ジェネリック ラムダに関する元の提案を参照してください。xvoida

肝心なのは、メンバー テンプレートのインスタンス化を完全に延期することは、(少なくとも 1 つの) 主要な実装で使用されるモデルと互換性がなく、それらが予期されるセマンティクスであるため、機能が導入されなかったということです。


それがこの制約の最初の動機でしたか? これは 1994 年 1 月から 5 月の間に導入されましたが、それをカバーする論文はありませんでした。そのため、ローカル クラスがテンプレート引数であってはならない理由に関するこの論文の正当化から、一般的な概念の大まかなアイデアを得ることができます。

クラス テンプレートとテンプレートから生成されたクラスは、グローバル スコープのエンティティであり、ローカル スコープのエンティティを参照できません。

おそらく当時、KISSしたかったのでしょう。

于 2016-10-25T15:28:08.977 に答える
3

問題はC++標準にもっとあると思います

正しい。これは、クラス テンプレートの [temp] で規定されています。

テンプレート宣言は、名前空間スコープまたはクラス スコープの宣言としてのみ表示できます。

メンバー テンプレートの [temp.mem]:

非クロージャ型のローカル クラスには、メンバ テンプレートがありません。


ラムダがローカル構造ではなく、テンプレート化されたメンバーを持つことが許可されている理由は何ですか?

C++11 でラムダを使用すると、その概念を拡張して汎用ラムダを使用することが非常に有用であると見なされたためです。このような言語拡張の提案があり、修正され 、修正されて採用されました。

一方、一般的なラムダでは適切に解決されないローカル クラスのメンバー テンプレートの必要性を説明する提案はまだ提示されていません (私が簡単な検索で知っている限り)。 .

これが解決する必要がある重要な問題であると思われる場合は、ローカル メンバー テンプレートが重要である理由について十分に考えた動機を説明した上で、遠慮なく提案を送信してください。

于 2016-10-25T15:23:36.870 に答える