20

私はこのようなことをしたいと思います:

int main()
{
    auto f = [/*some variables*/](/*take lambda function*/)
    {/*something with lambda function*/};

    f([/*other variables*/](/*variables to be decided by f()*/)
    {/*something with variables*/});
}

ラムダだけでなく関数にもラムダを渡すことができることを知っています。以下の作品:

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};

    f([](int i) -> double
    {return 0.0;});
}

しかし、以下は機能しません(スコープ変数を変更して[x]を追加するとすぐに)

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;}

    f([x](int i) -> double    //[x] does not work
    {return 0.0;});
}

エラーが発生します:

error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list
        argument types are: (lambda [](int)->double)
        object type is: lambda [](double (*)(int))->double

誰かがこれを修正する方法、またはそれを回避する方法について考えを持っていますか? std=c++11 で Intel コンパイラ icpc (ICC) 13.1.2 を使用しています

ありがとう

4

5 に答える 5

33

あなたの質問に関して明確にすることがいくつかあります。最初のものは、ラムダとは何ですか?

ラムダ式は、コンパイラが名前を付けることができない一意の型を生成する単純な式であり、同時にその型のインスタンスを生成します。あなたが書くとき:[](int i) { std::cout << i; }コンパイラはあなたのために大まかに次のような型を生成します:

struct __lambda_unique_name {
   void operator()(int i) const { std::cout << i; }
};

ご覧のとおり、関数ではなく、メンバー関数operator()として実装する型です。constラムダがキャプチャを行った場合、コンパイラは値/参照をキャプチャするコードを生成します。

まれなケースとして、上記のようなラムダの場合、キャプチャされる状態がない場合、言語は、ラムダ型から関数へのポインターへの変換をoperator()(部分を除いたthis) の署名で許可するため、上記のラムダは次のことができます。int何も返さずに受け取る関数へのポインタに暗黙的に変換されます。

void (*f)(int) = [](int i) { std::cout << i; }

基本が述べられたので、コードには次のラムダがあります。

auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;};

関数へのパラメーターの規則 (ラムダにも適用される) は、引数がfunction型であってはならないことを決定するため、ラムダへの引数は関数へのポインターに減衰します (配列型の引数がポインターに減衰するのと同じ方法で)。タイプ):

auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};

後で、引数としてキャプチャを持つラムダを渡そうとしています。キャプチャがあるため、特別なルールは適用されず、ラムダは関数へのポインターに変換できず、表示されるコンパイラ エラーが発生します。

現在の標準では、2 つの方法のいずれかを使用できます。type-erasure を使用して、署名から呼び出し可能なエンティティの正確なタイプを削除できます。

auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};

aは適切な署名を持つ任意の呼び出し可能なstd::function<double(int)>エンティティで初期化できるため、通常は動的割り当てと動的ディスパッチを意味する型消去を犠牲にして、以下のコードでラムダを受け入れます。

または、シンタックス シュガーを削除して、最初のラムダに相当するものを手動でロールすることもできますが、それをジェネリックにします。この場合、ラムダが単純な場合、これは有効なオプションになる可能性があります。

struct mylambda {
   template <typename F>
   double operator()(F fn) const {
      fn(0); return 0.0;
   }
} f;
// then use the non-lambda as you tried:
f([x](int i) -> double {return 0.0;});

最後に、辛抱強く待つことができる場合は、C++14 を待つことができます。C++14 では (ほとんどの場合、まだ承認されていません) 、上記のクラスの作成を簡素化するポリモーフィック ラムダがサポートされる予定です。

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above
于 2013-07-15T22:17:22.443 に答える
3

暗黒時代に私たちが行ったように、単に弾丸をかじって独自のファンクターを実装する必要があるかもしれません:

struct F {
    int x;
    int y;

    F(int x_, int y_) : x(x_), y(y_) {}

    template <typename G>
    double operator() (G&& g) const {
        g(0);
        return 0.0;
    }
};

#include <iostream>

int main()
{
    int x = 0;
    int y = 0;
    auto f = F(x, y);

    f([x](int i){return 0.0;});
    f([](int i){std::cout << i << std::endl;});
}

コンパイラが C++14 のジェネリック ラムダをサポートするまで、これで作業を続けることができます。

于 2013-07-15T21:59:34.867 に答える
1

たとえば、ラムダの型が事前にわかっている場合は、次のようなことを試すことができます。

int main()
{
    int x = 0, y = 0;

    auto f = [x]( int i )->double {
        return (double)x;
    };

    auto f2 = [x,y]( decltype(f) func )->double {
        return func( 0 );
    };

    f2( f );

    return 0;
}

または、より一般的なソリューションにライブラリを使用することもでき<functional>ます。たとえば、次のようになります。

auto f = [x,y]( std::function<double(int)> func ) { /* Do stuff */ };
于 2013-07-15T21:12:34.553 に答える