初識Lambda表示式(匿名函式)

MadAdam發表於2024-09-15

0.問題導向

使用C++ STL實現訂單按照建立時間從小到大排查。

using Order = struct tagOrder{
    unsigned int createTimspec;//建立時間戳
    int id;                    //訂單號
    int totalPrice;           //總價
    int status;               //訂單狀態
    int payType;              //支付型別
};
void orderSortByTime(vector<Order>& orders)
{
    sort(orders.begin(),orders.end());
}

這樣使用演算法庫的sort函式,編譯報錯:

error: no match for ‘operator<’ (operand types are ‘tagOrder’ and ‘tagOrder’)
 43 | return *__it1 < *__it2; }.

編譯器只預設支援對基本資料型別的大小比較,例如int,long等。自定義資料結構需要自定義比較函式。

using Order = struct tagOrder{
    unsigned int createTimspec;//建立時間戳
    int id;                    //訂單號
    int totalPrice;           //總價
    int status;               //訂單狀態
    int payType;              //支付型別
};
int compare(Order a,Order b)
{
    return a.createTimspec<b.createTimspec;
}
void orderSortByTime(vector<Order>& orders)
{
    sort(orders.begin(),orders.end(),compare);
}

你發現沒有,這個自定義的函式compare很簡單,只有一行程式碼。但是每次呼叫仍然有函式呼叫的開銷,如果系統中有大量的呼叫需求,那麼函式呼叫對效能的影響就不能忽視了。

using Order = struct tagOrder{
    unsigned int createTimspec;//建立時間戳
    int id;                    //訂單號
    int totalPrice;           //總價
    int status;               //訂單狀態
    int payType;              //支付型別
};
void orderSortByTime(vector<Order>& orders)
{
    sort(orders.begin(),orders.end(),[](Order a,Order b){return a.createTimspec<b.createTimspec;});
}

按照上圖方法也可以實現訂單排序功能,sort函式的第三個引數替代了自定義的compare函式,"[](Order a,Order b){return a.createTimspec<b.createTimspec;}",這串就是我們今天要學習的Lambda表示式

1.Lambda表示式簡介

Lambda表示式,也稱為Lambada函式,實際是一個匿名函式(不需要定義函式名),主要的使用場景是函式入參,例如演算法函式和非同步函式。在C++中,從C++11開始引入Lambda表示式,之後在後面的C++14、C++17、C++20、C++23版本對Lambda表示式都有更新和擴充套件。這裡我們選擇最初的C++11版本,學習下Lambda表示式的基本語法和用法。

2.Lambda表示式語法

Lambda基本語法如下,包含捕獲、引數、修飾符、返回型別和實體五部分。其中,修飾符和返回型別是可選部分。下面結合例子學習這五部分組成。
[capture](parameters) modifiers -> return-type {body}

3.Captures

1>捕獲引用型別和值型別

int arg=10;
auto lambdaCap = [&arg] (){arg++;};//capture by reference
lambdaCap();
cout<<"arg="<<arg<<endl;//arg=11
return 0;

上圖是捕獲引用型別,不用解釋。

int arg=10;
auto lambdaCap = [arg](){arg++;};//capture by value
lambdaCap();
cout<<"arg="<<arg<<endl;
//編譯失敗
main.cpp:265:30: error: increment of read-only variable ‘arg’

上圖編譯失敗。

int arg=10;
auto lambdaCap = [arg]()mutable{arg++;};//capture by value
lambdaCap();
cout<<"arg="<<arg<<endl;//arg=10

增加mutable修飾符後編譯透過,修飾符後面介紹。

2>捕獲有效範圍內的所有變數

 int arg=10;
 auto lambdaCap = [=]()mutable{arg++;};//capture by value
 lambdaCap();
 cout<<"arg="<<arg<<endl;//arg=10

上圖是使用'='表示以值型別的方式捕獲有效範圍內的所有變數。

 int arg=10;
 auto lambdaCap = [&]()mutable{arg++;};//capture by value
 lambdaCap();
 cout<<"arg="<<arg<<endl;//arg=11

上圖是使用'&'表示以引用的方式捕獲有效範圍內的所有變數。

4.Parameters

 auto lambdaAdd = [](int a,int b){return a+b;};
 cout<<"sum="<<lambdaAdd(1,2)<<endl;//sum=3

這裡的引數和函式的入參類似,入參也可以有預設值。

 auto lambdaAdd = [](int a,int b=1){return a+b;};//capture by value
 cout<<"sum="<<lambdaAdd(1)<<endl;//sum=2

5.Body

實體部分類似普通函式的函式體,例如開端的訂單排序例子中的Body返回建立時間的大小。

void orderSortByTime(vector<Order>& orders)
{
sort(orders.begin(),orders.end(),[](Order a,Order b){return a.createTimspec<b.createTimspec;});
}

6.Modifiers

可選部分,可以透過修飾符設定Lambda匿名函式的屬性,例如在Captures部分我們用的mutable修飾符。還有noexcept、noreturn等。

int arg=10;
auto lambdaCap = [arg]()mutable{arg++;};//capture by value
lambdaCap();
cout<<"arg="<<arg<<endl;//arg=10

7.Return-Type

可選部分,如果不設定返回型別,則由編譯器根據程式碼邏輯自動判斷型別,例如上面的所有例子都是這樣。如果設定返回型別,則強制要求編譯器按照該型別返回。

auto lambdaAdd = [](int a,int b)->long{return (long)a+b;};
cout<<"sum="<<lambdaAdd(0x7FFFFFFF,0x1)<<endl;//sum=2147483648

8.總結

這裡只是簡單介紹了Lambda函式的產生以及基本的語法和使用場景,還有很多深奧的相關部分需要持續學習。日拱一卒無有盡,終不唐捐入海流。

相關文章