2016年5月8日 星期日

C++11 採用 Lambda 作為回叫函數的用法

先看一個簡單的函數定義,這函數須要一個函數指標作為參數

// 須傳入指向函數的指標
int ff1( int(*fp)(int) )
{
  return fp(5);
}

可以如下使用沒有問題

// 符合 ff1 所要求的規格
int Backf(int i)
{
  return i * 5;
}

int main()
{
  // 一般用法
  cout << ff1(Backf) << endl;

  // 採用 Lambda 的用法
  cout << ff1([](int c)->int {return c * 2; }) << endl;

  return 0;
}


但 ff1() 用 Lambda 為參數的用法,只限沒有「擷取子句」(Capture Clause),它就會像一般函數一樣,若有擷取子句編譯時就會有錯誤訊息

int main()
{
  int i = 10;

  cout << ff1([i](int c)->int {return c * 2 + i; }) << endl;

/*
有錯誤訊息:
  無法將引數 1 從 'main::<lambda_02198bfdd2c666dbefc4a599da39b9cb>'
  轉換為 'int (__cdecl *)(int)'
*/

  return 0;
}

主要在有擷取子句的 Lambda 相當於一個函數物件(function object)的用法,因此上例的有擷取子句的 Lambda 相當於如下的 class

class Lambda_Obj
{
  int m_i;

public:
  // Constructor
  Lambda_Obj(int i)
  {
    m_i = i;
  }

  int operator()(const int c) const
  {
    return c * 2 + m_i;
  }
};



為了能因應各種型別,ff1() 須改成 template function 的樣式

// 參數須傳入 int(*fp)(int) 的樣式
template<typename F>
int ff2(F fp)
{
  return fp(5);
}

這樣使用上就沒問題了

int main()
{
  // 一般用法
  cout << ff2(Backf) << endl;

  // 採用無 擷取子句 Lambda 的用法
  cout << ff2([](int c)->int {return c * 2; }) << endl;

  // 採用有 擷取子句 Lambda 的用法
  int i = 10;
  cout << ff2([i](int c)->int {return c * 2 + i; }) << endl;

  // Lambda_Obj 的用法
  Lambda_Obj Func_Obj(10);
  cout << ff2(Func_Obj) << endl;

  return 0;
}


Lambda 參考資料:
http://blog.gtwang.org/programming/lambda-expression-in-c11/
https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0 




沒有留言:

張貼留言