Lambda

20 February 2022

lambda

lambda function可以想像成是C++ anonymous function的實現,常見用途在stl function call (如sort, find_if, count…)的callback function, 又或者是當我們需要用到現在block裡面多個變數的運算時,又希望能做封裝,那就會用lambda。 範例:

bool cmp(int a, int b) { return a < b;}

int main(){
    sort(v.begin(),v.end(),cmp); // cmp is only used here
}

在lambda function之前我們要define一個cmp function定義比對方式,然後把這個function填入sort的最後一個參數。

stl function使用cmp function的問題

  1. 定義的cmp function其實就只是單純用於這個stl的function callback, 沒有其他的用途,為此宣告一個function有點太繁瑣。
  2. 當有另一個比對方式或者比對的type, 等於我又要宣告定義另一個cmp function, 那這樣結果就是會變成很多的cmp function宣告跟定義,變得很多(如以下範例)
bool cmpi(int a, int b) { return a < b;}
bool cmpi(int a, int b) { return a < b;}

int main(){
    sort(v.begin(),v.end(),cmpi);
    
    //use lambda
    sort(v.begin(),v.end(),[](int a, int b){return a < b;});
}

lambda function讓我們不用特地寫一個function當作參數傳入,而可以更簡單地把cmp的definition寫在sort的參數裡, 又或者是可以宣告在main block裡, 那就代表這個function只在這個main block有效

Capture Clause
這是lambda function的另一大優勢,當我們寫的小function會需要使用到很多variable時,一個作法是把會用到的variable當作parameter傳進來,要不然就是把他變成global variable, 但這兩種方式都不太好,越多的global variable 可能multithread會產生race condition, 又或者多個function parameter會讓function interface變得很複雜。

以往較好的作法可能是為此create一個class, 把要用的東西做成class member, 真正跟function有關的變數才放到class method的argument上,但這樣的語法非常麻煩,為了做到一件事情要寫非常多行的code。

lambda對於會用到的variable可以使用capture clause, 把他透過pass by value/reference形式傳入, 把他跟function argument分開,這樣function的interface就還是很單純,然後用到的variable也不用放在global variable, lambda可以存取同個block裡的變數

完整語法

auto f1 = [] () mutable -> int {}
          1  2  3           4   5

1為capture clause, 可以決定有哪些外部的變數要傳進lambda body, 可以為=, & 分別為pass by value/ pass by reference
3為修飾語法(可以為constexpr, mutable)
4為trailing return type, 宣告return type為何

Generic lambda

C++ 14後提供了template版的lambda function, 也就是同樣的邏輯但不同data type的運算(template) 可以使用generic lambda來實作, 這樣就只需要一個lambda function

// C++11: lambda
auto icmp = [] (int a, int b) { return a < b; };
auto scmp = [] (std::string a, std::string b) { return a < b; };
// C++14: generic lambda
auto cmp = [] (auto a, auto b) { return a < b; };

沒有capture的lambda function也可以退化成function pointer, 所以也可以用一個有固定data type的function pointer指向generic lambda:

auto cmp = [] (auto a, auto b) { return a < b; };
bool(*icmp)(int, int) = cmp;

Reference