標籤:lambda c++
我們知道lambda運算式的行為很像是是一個匿名函數,我們常常在標準演算法中使用lambda運算式。比如需要列印一個向量,可能會這樣寫:
vector<int> v(10, 10);std::for_each(v.begin(), v.end(), [](int n){ cout << n << " ";});
使用了for_each演算法針對 v 中的每一個元素使用lambda運算式,並且成功的達到了目的。
但是看看下面這個方式也可以完成任務:
std::for_each(v.begin(), v.end(), print_int);
這裡的print_int 是什麼呢?多數人都會知道它是一個形如void print_int(int)的函數,沒錯,但是他還可以是另外一類截然不同的類型,也就是說它並不是一個函數。
在學習類的運算子多載的時候,很多人可能會忽略掉()的重載,也就是括弧的重載。我們知道對類的括弧進行重載就能想函數一樣使用類對象。舉例如下:
class Print{ void operator()(int n){ cout << n << " "; }};Print print_int; //聲明類對象print_int(3); //使用了()運算子
這樣我們將3列印出來了。所以上面for_each語句中的print_int也可以是一個類對象。說這些的目的只是為了引出下面的話題,那就是lambda運算式的本質是什嗎?
其實在C++中,lambda運算式都被編譯器翻譯成了一個未命名的類的一個未命名物件。上面的lambda運算式,就被翻譯成了形如Print類的一個未命名類,而在for_each中,聲明了一個未命名的對象,然後使用其括弧運輸符來完成 v 中各個元素的處理。
我們知道,lambda運算式前面的 [] 是捕獲列表,而捕獲方式有通過引用捕獲和通過值捕獲。當通過值引用的時候,編譯器在建立類的時候,會將這個值作為類成員變數,而在聲明未命名物件的時候,將這個類成員初始化為所引用的變數當前的值。
當通過引用的方式捕獲時,編譯器不會將其儲存為類成員。下面通過一個例子來說明以上內容。
int value = 100;auto it = find_if(v.begin(),v.end(),[value](const int n){ return n<value;});cout<<*it;
上面這段代碼是為了找到 v 中小於 100 的第一個數,這裡我將長度最大長度按值傳遞給lambda運算式。編譯器為之可能產生下面這個類:
class XXX{public: XXX(int x):value(x){} //這裡的x就是捕獲列表中的變數 --- [value] bool operator()(const int n)const{ //這裡與lambda的參數,傳回值,函數體一致 return n<value; }private: int value;};
之後,我們可以這樣做:
int value = 100;XXX xx(value);auto it = find_if(v.begin(), v.end(), xx);cout<<*it;
毫無疑問,這裡find_if對v中每一個元素調用了xx.operator(int n),通過傳回值得到了指向第一個小於value的值的迭代器。
其實你還可以這樣使用XXX類:
auto it = find_if(v.begin(), v.end(), XXX(100));
現在你或許對lambda運算式有有了一些認識,所以不妨自己定義一個這樣的類來測試一下,是否真的如我這裡所說。
C++中lambda的實質是什麼?