1. 程式人生 > 其它 >C++之Lambda表示式詳細解釋

C++之Lambda表示式詳細解釋

技術標籤:C++進階之戰c++lambda指標

Lambda表示式

Lambda表示式格式

C++ 11 中的 Lambda 表示式用於定義並建立匿名的函式物件,以簡化程式設計工作。

Lambda 的語法形式如下:

[函式物件引數](操作符過載函式引數)mutable或exception宣告->返回值型別{函式體}

程式碼格式:

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>

usingfptr=int(*)(int&obj);

intmain()
{
fptrfobj=[](int&obj)mutablethrow(int)->int{returnobj;};
}

可以看到,Lambda 主要分為五個部分:[函式物件引數]、(操作符過載函式引數)、mutable 或 exception 宣告、-> 返回值型別、{函式體}。

捕獲列表:[捕獲引數列表]

標識一個 Lambda 表示式的開始,這部分必須存在,不能省略。函式物件引數是傳遞給編譯器自動生成的函式物件類的建構函式的。函式物件引數只能使用那些到定義 Lambda 為止時 Lambda 所在作用範圍內可見的區域性變數(包括 Lambda 所在類的this)。函式物件引數有以下形式:

① 空:不捕獲任何外部引數,捕獲其實就是“把作用域內的變數拿來在函式體中直接用的意思”;

② =:函式體內可以使用 Lambda 所在範圍內所有可見

的區域性變數(包括 Lambda 所在類的 this),並且是值傳遞方式(相當於編譯器自動為我們按值傳遞了所有區域性變數);

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>

intmain()
{
inta=10;
vector<int>obj{1,2,3,4,5};
for_each(obj.begin(),obj.end(),[=](int&obj)mutable{obj+=a;cout<<obj<<endl;});
}

用值傳遞的方式傳遞一個變數:

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>

intmain()
{
inta=10;
vector<int>obj{1,2,3,4,5};
for_each(obj.begin(),obj.end(),[a](int&obj)mutable{obj+=a;cout<<obj<<endl;});
}

注:

Mutable的作用是可以在lambda函式體內部修改捕獲列表捕獲的變數值,但是由於我這裡是按照值傳遞的方式傳入引數,因此在lambda函式體內部改變a並不會引起外部a的變化。

③ &:函式體內可以使用 Lambda 所在範圍內所有可見的區域性變數(包括 Lambda 所在類的 this),並且是引用傳遞方式(相當於是編譯器自動為我們按引用傳遞了所有區域性變數);

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>

intmain()
{
inta=10;
vector<int>obj{1,2,3,4,5};
for_each(obj.begin(),obj.end(),[&](int&obj)mutable{obj+=a;cout<<obj<<endl;});
}

用引用的方式捕獲單個變數:

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>

intmain()
{
inta=10;
vector<int>obj{1,2,3,4,5};
for_each(obj.begin(),obj.end(),[&a](int&obj)mutable{obj+=a;cout<<obj<<endl;});
}

④ this:函式體內可以使用 Lambda 所在類中的成員變數;

#include<iostream>
usingnamespacestd;
#include<algorithm>
#include<vector>
#include<string>
#include<functional>

classPerson
{
public:
intage;
stringname;
public:
Person(intage,stringname)
{
this->age=age;
this->name=name;
}
voidShowInf()
{
function<void()>fptr=[this](){cout<<this->name<<"的年齡為"<<this->age<<endl;};
fptr();//呼叫lambda函式
}
};

intmain()
{
Personobj(12,"張三");
obj.ShowInf();
}

注意:

⑴ 這裡的lambda表示式的原型是function<返回資料型別(引數資料型別)>,和C語言中的函式指標有異曲同工之妙,其實兩者本質上差不多,只不過lambda表示式多了個“捕獲列表”而已;

⑵ 切記:由於我們傳入的是this指標,因此類中不可以呼叫靜態成員!

⑤ a,&b:將 a 按值傳遞,b 按引用進行傳遞;

⑥ =,&a,&b。除 a 和 b 按引用進行傳遞外,其他引數都按值進行傳遞;

⑦ &,a,b。除 a 和 b 按值進行傳遞外,其他引數都按引用進行傳遞。

引數列表:(函式引數列表)

標識過載的 () 操作符的引數,沒有引數時,這部分可以省略。引數可以通過按值(如: (a, b))和按引用 (如: (&a, &b)) 兩種方式進行傳遞。

mutable 或 exception 宣告

這部分可以省略。按值傳遞函式物件引數時,加上 mutable 修飾符後,可以修改傳遞進來的拷貝(注意是能修改拷貝,而不是值本身)。exception 宣告用於指定函式丟擲的異常,如丟擲整數型別的異常,可以使用 throw(int)。

-> 返回值型別

標識函式返回值的型別,當返回值為 void,或者函式體中只有一處 return 的地方(此時編譯器可以自動推斷出返回值型別)時,這部分可以省略。

{函式體}

標識函式的實現,這部分不能省略,但函式體可以為空。

將匿名函式例項化

把匿名函式儲存在變數、陣列或 vector 中,並把它們當做命名引數來傳遞:

#include<iostream>
usingnamespacestd;
#include<functional>
#include<vector>

intmain()
{
function<void(int,int)>fptr=[](intobj1,intobj2){cout<<obj1+obj2<<endl;};//必須引數型別一一對應
fptr(1,1);
vector<function<void(int,int)>>obj{fptr};
obj[0](1,2);
function<void(int,int)>fptr_array[1]{fptr};
fptr_array[0](4,6);
}

Lambda匿名函式與函式指標相互轉化

#include<iostream>
usingnamespacestd;
#include<functional>

usingf_ptr=void(*)(int,int);

intmain()
{
function<void(int,int)>fptr=[](intobj1,intobj2){cout<<obj1+obj2<<endl;};
f_ptrfptr1=[](intobj1,intobj2){cout<<obj1+obj2<<endl;};
//f_ptr=fptr;//不存在function<void(int,int)>物件轉化至void(*)(int,int)物件
fptr1(1,3);
}

注意:由於lambda表示式比函式指標更高階,即多了一個“可以捕獲外部資料” 的功能,因此,我們在將lambda表示式賦值給對應型別的函式指標時,捕獲列表[]中一定要為空!