web-dev-qa-db-ja.com

引数としてラムダ式を受け入れる関数を記述します

私はこのような方法を持っています

template<typename T, typename U>
map<T,U> mapMapValues(map<T,U> old, T (f)(T,U))
{
    map<T,U> new;
    for(auto it = old.begin(); it != old.end(); ++it)
    {
        new[it->first] = f(it->first,it->second);
    }
    return new; 
}

そして、あなたはこのようにそれを呼ぶだろうという考えです

BOOST_AUTO_TEST_CASE(MapMapValues_basic)
{
    map<int,int> test;
    test[1] = 1;
    map<int,int> transformedMap = VlcFunctional::mapMapValues(test, 
        [&](int key, int value) -> int
        {
            return key + 1; 
        }
    );
}

ただし、エラーが発生します:関数テンプレート「VlcFunctional :: mapMapValues」のインスタンスが引数リストに一致しません。引数のタイプは次のとおりです:(std :: map、std :: allocator >>、__lambda1)

私が間違っていることは何か考えていますか? Visual Studio 2008およびIntel C++コンパイラー11.1

37
Jamie Cook

関数はラムダではなく関数ポインターを期待しています。

C++では、一般に3つのタイプの「呼び出し可能オブジェクト」があります。

  1. 関数ポインタ。
  2. 関数オブジェクト。
  3. ラムダ関数。

これらすべてを関数インターフェイスで使用できるようにする場合は、std::functionを使用できます。

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T, U)> f)
{
    ...
}

これにより、上記の3種類の呼び出し可能なオブジェクトのいずれかを使用して関数を呼び出すことができます。ただし、この利便性の代償として、関数の呼び出しに伴うオーバーヘッドがわずかに発生します(通常は、ヌルポインターのチェック、その後の関数ポインターによる呼び出し)。これは、関数がほぼ確実にインライン化されないことを意味します(多分高度な [〜#〜] wpo [〜#〜])を除く / [〜#〜] lto [〜#〜] )。

あるいは、2番目のパラメーターに任意の型を使用するために、追加のテンプレートパラメーターを追加することもできます。これはより効率的になりますが、使用する関数の型安全性が失われ、コードの肥大化につながる可能性があります。

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
40
Peter Alexander

パラメーターの型宣言T (f)(T,U)は、型が 'TUを取り、Tを返す' free functionです。ラムダ、関数オブジェクト、またはそのシグネチャを持つ実際の関数以外のものを渡すことはできません。

これを解決するには、次のようにパラメータのタイプをstd::function<T(T,U)>に変更します。

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T,U)>)
{
}

または、次のように、関数の型をテンプレート引数として宣言することもできます。

template<typename T, typename U, typename Fn> 
map<T,U> mapMapValues(map<T,U> old, Fn fn)
{
  fn(...);
}
12
JoeG

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

// 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 ();

} // ()
10
cibercitizen1

n3052 によると、空のキャプチャリストを持つラムダ式は関数ポインタに減衰する必要があります。ただし、この機能はVC++では実装されておらず、g ++では部分的にしか実装されていないようです。mySO question を参照してください。

6
rafak

これは、関数をパラメーターとして渡す方法の例です。

class YourClass
{
void YourClass::callback(void(*fptr)(int p1, int p2))
{
    if(fptr != NULL)
      fptr(p1, p2);
}
};

void dummyfunction(int p1, int p2)
{
   cout << "inside dummyfunction " << endl;
}

YourClass yc;

// using a dummyfunction as callback
yc.callback(&dummyfunction);

// using a lambda as callback
yc.callback( [&](int p1, int p2) { cout << "inside lambda callback function" << endl; } );

// using a static member function 
yc.callback( &aClass::memberfunction );
1
serup