ラムダを受け入れる関数を宣言するにはどうすればよいですか?


82

標準ライブラリ(などstd::find)でラムダを使用する方法を説明する多くのチュートリアルをインターネットで読みました。それらはすべて非常に興味深いものでしたが、自分の関数にラムダを使用する方法を説明するチュートリアルは見つかりませんでした。

例えば:

int main()
{
    int test = 5;
    LambdaTest([&](int a) { test += a; });

    return EXIT_SUCCESS;
}

どのように宣言すればよいLambdaTestですか?その最初の引数のタイプは何ですか?次に、引数として「10」を渡す無名関数を呼び出すにはどうすればよいですか?

回答:


78

ラムダに加えて関数ポインターと関数オブジェクトも受け入れたいと考えている場合は、テンプレートを使用して、operator()。を含む引数を受け入れたいと思うでしょう。これは、findのようなstd関数が行うことです。次のようになります。

template<typename Func>
void LambdaTest(Func f) {
    f(10);
}

この定義はc ++ 0x機能を使用しないため、完全な下位互換性があることに注意してください。c ++ 0x固有のラムダ式を使用した関数の呼び出しのみです。


2
エラーが発生した場合、エラーメッセージを理解するのは困難です。
liori 2010年

13
それが最高かどうかによります。これはテンプレートを使用しますが、他は使用しません。これは、関数を仮想化できなくなり、cppファイルで個別に定義できないことを意味します。std::function呼び出すときは少し遅くなりますが、関数オブジェクトクラス型も完全に受け取ることができます。しかし、その違いはほとんどのアプリケーションではごくわずかです:)
Johannes Schaub-litb 2010年

2
「最高」は見る人の目にあります:-)この答えfunctorは素晴らしいaを使用していますが、「自分の関数にラムダをどのように使用するか」という元の質問(およびここで私を導いた理由)には実際には答えていません。 。また、テンプレートには独自の問題がありますが、答えにstd::functionはありません。
Marco Massenzio 2017

1
@Marcoこの回答では、関数を使用する必要はありません。ラムダを含め、好きなものを使用できます。
sepp2k 2017

68

すべてをテンプレート化したくない場合は、次のようにすることができます。

void LambdaTest (const std::function <void (int)>& f)
{
    ...
}

1
この構文により、実際には関数変数を保存して後で呼び出すことができますよね?たとえば、ラムダがコールバックとして機能する非同期データベースクエリの実行を許可する関数を実装したいと思いました。(もちろん、参照によってクロージャにアクセスすることはできません)
Thomas Bonini 2010年

1
関数を値で渡すのはもっと慣用的ではありませんか?
fredoverflow 2010年

1
@Andreas Bonini:はい、std::function(参照ではなく)に保存すると、のコピーが作成されますf。ただし、参照されているオブジェクトがスコープ外(おそらくUB)になったときに、ラムダ/クロージャが参照をどのように処理するかはわかりません。@FredOverflow:私の理解ではstd::function、特にラムダをラップする場合、それは些細なオブジェクトではありません。不必要なコピーを避けるために、それを参照する方がおそらく良いでしょう。
2010年

ラムダがスタックを値でキャプチャする場合、はい、ラムダはそれらの変数よりも長持ちし、それらのコピーを保持し続けます。参照によってキャプチャすると、問題が発生します。
ケイトグレゴリー

1
関数内でコピーする必要があるため、渡されたときにコピーしても意味がありません
Casebash 2011年

9

この単純ですが自明の例を提供したいと思います。「呼び出し可能なもの」(関数、関数オブジェクト、ラムダ)を関数またはオブジェクトに渡す方法を示します。

// g++ -std=c++11 thisFile.cpp

#include <iostream>
#include <thread>

using namespace std;

// -----------------------------------------------------------------
class Box {
public:
  function<void(string)> theFunction; 
  bool funValid;

  Box () : funValid (false) { }

  void setFun (function<void(string)> f) {
    theFunction = f;
    funValid = true;
  }

  void callIt () {
    if ( ! funValid ) return;
    theFunction (" hello from Box ");
  }
}; // class

// -----------------------------------------------------------------
class FunClass {
public:
  string msg;
  FunClass (string m) :  msg (m) { }
  void operator() (string s) {
    cout << msg <<  s << endl; 
  }
};

// -----------------------------------------------------------------
void f (string s) {
  cout << s << endl;
} // ()

// -----------------------------------------------------------------
void call_it ( void (*pf) (string) ) {
  pf( "call_it: hello");
} // ()

// -----------------------------------------------------------------
void call_it1 ( function<void(string)> pf ) {
  pf( "call_it1: hello");
} // ()

// -----------------------------------------------------------------
int main() {

  int a = 1234;

  FunClass fc ( " christmas ");

  f("hello");

  call_it ( f );

  call_it1 ( f );

  // conversion ERROR: call_it ( [&] (string s) -> void { cout << s << a << endl; } );

  call_it1 ( [&] (string s) -> void { cout << s << a << endl; } );

  Box ca;

  ca.callIt ();

  ca.setFun (f);

  ca.callIt ();

  ca.setFun ( [&] (string s) -> void { cout << s << a << endl; } );

  ca.callIt ();

  ca.setFun (fc);

  ca.callIt ();

} // ()

2
funValidは必要ありません:en.cppreference.com/w/cpp/utility/functional/function/…、ただ言うif ( ! theFunction )
Erik Aronesty 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.