web-dev-qa-db-ja.com

ラムダ式をラムダ引数c ++ 11に渡す

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

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

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

ありがとう

19
Couchy311

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

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

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

ご覧のとおり、それはnot関数ですが、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;};
_

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

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

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

現在の標準では、2つの方法のいずれかを使用できます。タイプ消去を使用して、呼び出し可能なエンティティの正確なタイプをシグネチャから削除できます。

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

std::function<double(int)>は、適切なシグネチャを持つ任意のcallableエンティティで初期化できるため、型消去の代償として、以下のコードでラムダを受け入れますこれは通常、動的割り当てと動的ディスパッチを意味します。

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

_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を待つことができます(ほとんどの場合、まだ承認されていません)。単純化する多相ラムダがサポートされます。上記のクラスの作成:

_auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above
_

Std :: functionを使用してみてください:

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

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

暗黒時代に行ったように、弾丸を噛んで独自のファンクタを実装する必要がある場合があります。

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ジェネリックラムダをサポートするまで、これで続行できます。

3
Casey

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

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 */ };
1
Thomas Russell

キャプチャするラムダを指定できますが、このソリューションには制限があります。

#include <new>

#include <utility>

namespace
{

template <typename F, int I, typename L, typename R, typename ...A>
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
  std::declval<F>()(std::declval<A>()...))))
{
  static L l_(std::forward<L>(l));
  static bool full;

  if (full)
  {
    l_.~L();

    new (static_cast<void*>(&l_)) L(std::forward<L>(l));
  }
  else
  {
    full = true;
  }

  return [](A... args) noexcept(noexcept(
      std::declval<F>()(std::forward<A>(args)...))) -> R
    {
      return l_(std::forward<A>(args)...);
    };
}

}

template <typename F, int I = 0, typename L>
inline F cify(L&& l)
{
  return cify<F, I>(std::forward<L>(l), F());
}


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

    f(cify<double(*)(int i)>([x](int i) -> double    //works now
    {return 0.0;}));
}

クリック 作業例。

0
user1095108