匿名函數(shù)--lambda函數(shù)
????匿名函數(shù)或者匿名類這種語法在其他語言(如lisp,java中)早有應(yīng)用。在C++11中正式引入了lambda函數(shù),在很多場(chǎng)景下讓程序變得更加直觀,下面我們將C++中的lambda函數(shù)語法規(guī)則做一個(gè)梳理。
????首先先看一段代碼,直觀感受一下lambda的使用。
#include <iostream>
template <typename T, typename R>
void foo(T func, R param)
{
func(param);
}
void bar(int param)
{
std::cout << "function param is :" << param << std::endl;
}
struct foobar
{
void operator ()(int param)
{
std::cout << "functor param is :" << param << std::endl;
}
};
int main()
{
foo([](int param)->void
{
std::cout << "lambda param is :"<<param<<std::endl;
}, 123);
foo(bar, 456);
foo(foobar(), 789);
return 0;
}
????上面代碼展示了一個(gè)模板函數(shù)接受三類可執(zhí)行代碼(函數(shù)、仿函數(shù)和lambda函數(shù))的示例。foo為模板函數(shù),它接受兩個(gè)參數(shù),第一參數(shù)是可執(zhí)行代碼,第二參數(shù)是第一參數(shù)的參數(shù)。我們的調(diào)用代碼展示了它的用法,它可以接受lambda函數(shù)、普通函數(shù)和仿函數(shù)??梢钥闯?,接受lambda函數(shù)版本的調(diào)用直接在調(diào)用處展開,這種方式比較直觀,如果習(xí)慣了java語言的同學(xué)應(yīng)該更有體會(huì)。
????lambda的基礎(chǔ)語法定義如下:
[capture](parameters) mutable ->return-type{statement}
其中,
1、[capture]:捕獲列表。它總是出現(xiàn)在lambda函數(shù)的開始位置。在編譯器看來[]是lambda的引出符號(hào),編譯器正式通過它來判斷接下來的代碼是否是lambda函數(shù)。捕獲列表能夠捕捉當(dāng)前上下文中的變量供給lambda函數(shù)使用。具體的capture列表中的語法,下面還會(huì)詳細(xì)講述。
2、(parameters):參數(shù)列表。它跟一般函數(shù)的參數(shù)列表一樣,使用規(guī)則也相同。在lambda中,如果不需要傳入?yún)?shù),可以省略。
3、mutable:修飾符。默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消它的常量屬性。顯示指定mutable修飾符的時(shí)候,參數(shù)列表不能省略。
4、->return-type:返回值類型。->這個(gè)同C++11新引入的追蹤返回值類型的聲明是一致的,語法也是一致的。不同的是,處于方便,lambda函數(shù)在沒有返回值的情況下,可以省略掉(在某些編譯器可以推導(dǎo)出返回值類型的情況亦可省略)。
5、{statement}:函數(shù)體。與一般函數(shù)的函數(shù)體一致,額外可以使用捕獲列表中捕獲的變量。
????上面就是lambda函數(shù)的語法,可以看出2和3、4都是可選的,所以一個(gè)lambda函數(shù)可以簡(jiǎn)化成為下面的代碼
[]{std::cout<<"Hello Lambda\n";}
????lambda函數(shù)相較普通函數(shù)調(diào)用最便捷之處就是其捕獲列表,它可以通過值傳遞捕獲或者引用傳遞方式捕獲,直接在函數(shù)體內(nèi)訪問到上下文(一個(gè)代碼塊內(nèi))的變量。如果是普通函數(shù)的話,這些都要以參數(shù)形式傳遞進(jìn)去,使代碼十分冗長(zhǎng)。那么捕獲列表的具體語法可以歸納如下:
1、[a]表示值傳遞捕獲變量a(多個(gè)參數(shù)可以用逗號(hào)分隔)
2、[=]表示值傳遞捕獲上下文所有變量
3、[&a]表示引用傳遞捕獲變量a
4、[&]表示引用傳遞捕獲上下文所有變量
5、[this]表示值傳遞捕獲當(dāng)前的this指針
6、[=, &a, &b]表示值傳遞捕獲上下文所有變量,但是a、b變量以引用傳遞方式捕獲。
7、[&, a, this]表示引用傳遞捕獲上下文所有變量,但是a和this指針以值傳遞方式捕獲。
char c = 'a';
float d = 1.11f;
foo([=](int param)->void
{
std::cout << "lambda param is :"<< param <<std::endl;
std::cout << "lambda cap is :" << c << std::endl;
std::cout << "lambda cap2 is :" << d << std::endl;
}, 123);
上面代碼展示了捕獲列表的值傳遞用法,其余方式大同小異。
lambda函數(shù)和仿函數(shù)
????在C++98STL庫的實(shí)現(xiàn)中,大量的函數(shù)需要傳入回調(diào),而這些回調(diào)基本都是以模板參數(shù)的形式傳入。這樣的好處是既可以接受C編程習(xí)慣中的回調(diào)函數(shù),又可以接受C++方式的functor(仿函數(shù))?,F(xiàn)在有了lambda函數(shù),我們有了第三種選擇。文章最開始的例子實(shí)際上已經(jīng)展示了三種方式傳入給模板函數(shù)的情況,我們現(xiàn)在以STL中的for_each為例再來看一下lambda函數(shù)和仿函數(shù)之間的對(duì)比。
struct for_each_functor
{
void operator ()(int param)
{
std::cout << param + 5 << std::endl;
}
};
int main()
{
std::vector<int> vec_foo;
for (int i = 0; i < 10; ++i)
{
vec_foo.push_back(i);
}
std::for_each(vec_foo.begin(), vec_foo.end(), for_each_functor());
std::for_each(vec_foo.begin(), vec_foo.end(),
[](int param)->void
{
std::cout << param + 15 << std::endl;
}
);
return 0;
}
????看一下上面的代碼,兩種for_each的調(diào)用,是不是感覺lambda版本的調(diào)用看起來更加簡(jiǎn)潔,并且實(shí)現(xiàn)起來也比functor版本簡(jiǎn)潔了一些。
最后
????lambda函數(shù)的語法比較簡(jiǎn)單,但是它跟實(shí)際應(yīng)用結(jié)合在一起就會(huì)發(fā)揮很大的作用,給程序編寫帶來很大的便利。后面我們將會(huì)介紹的基于C++11的線程池中,會(huì)極致利用lambda函數(shù)的便利性,使得我們可以用很少的代碼編寫一個(gè)極其方便、功能極其強(qiáng)大的線程池類。