6

明示的に型指定された変数にラムダを代入するとき (たとえば、関数自体をキャプチャするために再帰的である場合) を使用しますstd::function

例として、このばかげた「ビット カウント」関数を考えてみましょう。

std::function<int(int)> f;
f = [&f](int x){ return x ? f(x/2)+1 : 0; };

xC++14 ジェネリック ラムダで導入されたように、 auto パラメーターを使用して を一般化する場合はどうなるでしょうか?

std::function<int(???)> f;
f = [&f](auto x){ return x ? f(x/2)+1 : 0; };

明らかにautofunction型パラメーターを配置することはできません。

上記の正確なケースをカバーするのに十分な一般的なファンクタークラスを定義する可能性はありますが、関数定義にはまだラムダを使用していますか?

(これを過度に一般化しないでください。単一の auto パラメータのみを受け入れ、戻り値をハードコーディングします。) 使用例は、上記のようなシナリオの場合です: 再帰呼び出しの参照によって関数自体をキャプチャします。

4

2 に答える 2

3

以下は、簡単な y コンビネータ ベースの再帰エンジンです。

template<class F>
struct recursive_t {
  F f;

  // note Self must be an lvalue reference.  Things get
  // strange if it is an rvalue:
  // invoke makes recursive ADL work a touch better.
  template<class Self, class...Args>
  friend auto invoke( Self& self, Args&&...args )
  -> decltype( self.f( self, std::declval<Args>()... ) )
  {
    return self.f( self, std::forward<Args>(args)... );
  }
  // calculate return type using `invoke` above:
  template<class Self, class...Args>
  using R = decltype( invoke( std::declval<Self>(), std::declval<Args>()... ) );

  template<class...Args>
  R<recursive_t&, Args...> operator()(Args&&...args)
  {
    return invoke( *this, std::forward<Args>(args)... );
  }
  template<class...Args>
  R<recursive_t const&, Args...> operator()(Args&&...args)const
  {
    return invoke( *this, std::forward<Args>(args)... );
  }
};

template<class F>
recursive_t< std::decay_t<F> > recurse( F&& f )
{
  return {std::forward<F>(f)};
}

今、あなたはできる:

auto f = recurse( [](auto&& f, auto x){ return x ? f(x/2)+1 : 0; } );

そして、キャプチャを持たない再帰ラムダを取得します&(これにより、その使用が現在のスコープに制限されます)。

参照によってa をキャプチャするというstd::functionことは、ラムダの有効期間が現在のスコープであることを意味し、すべての再帰呼び出しで型消去を行う必要があります (再帰呼び出しで末尾再帰などの可能な最適化をブロックします)。同じことが他の同様のソリューションにも当てはまります。

recursive_tラムダはそれ自体で名前を付けることができないため、ラムダを使用するのではなく、 を使用する必要があります。

実例

ラムダベースのバージョンは、実装がやや簡単です。変更可能なラムダと不変のラムダには、異なる型の関数が必要になることに注意してください。

template<class F>
auto recurse( F&& f ) {
  return [f=std::forward<F>(f)](auto&&...args){
    return f(f, decltype(args)(args)...);
  };
};

次のrecursive_tような作品:

auto fib = recurse( [](auto&& fib, int x){ if (x<2) return 1; return fib(x-1)+fib(x-2); } );

ラムダ版は次のように機能します。

auto fib = recurse( [](auto&& self, int x){ if (x<2) return 1; return self(self, x-1)+self(self,x-2); } );

私は、個人的にはもっと厄介だと思います。

の型を記述するのも難しいですrecurserecursive_tバージョンの場合、recurseタイプは次のとおりです。

((A->B)->A->B)->(A->B)

これは厄介ですが、有限型です。

ラムダ版はよりトリッキーです。関数の引数recursiveの型は次の型です。

F:= F->A->B

これはいらいらするほど無限であり、次にrecurseタイプです

F->A->(A->B)

無限を受け継ぐもの。

とにかく、recurse戻り値は通常std::functionの に格納するか、型消去されたコンテナに格納しないことができます。

于 2015-07-08T15:12:42.327 に答える