必须使用列别名命名此表达式_lambda表达式
一般的,如果一個類定義了函數調用運算符,則我們可以像使用函數一樣使用這個類,例如:一個類A定義了函數調用運算符,我們就可以使用A()這樣的形式調用對象,實際上調用了類的調用運算符函數。如果一個類定義了調用運算符,則該類的對象可以被稱為函數對象(可調用對象包含:函數,函數指針,重載了函數調用運算符的類和lambda表達式)。在lambda表達式中,編譯器將改表達式翻譯為一個未命名的類的未命名對象。lambda表達式產生的類中含有一個重載的函數調用運算符。默認情況下,lambda不能改變它捕獲的變量(可以通過mutable關鍵字進行修改,后面會介紹)。因此,由lambda表達式產生的類中含有一個函數調用運算符的const成員函數(const成員函數有常量對象調用,一般不改變類的成員變量,例如get一個成員變量,應該在函數的參數列表后加上const關鍵字)。lambda表達式產生的類中不包含默認構造函數,賦值運算符和默認析構函數。是否含有默認的拷貝/移動構造函數則通常要視捕獲的對象的類型而定。
lambda表達式來自于lambda演算,其定義如下:
λ演算_百度百科?baike.baidu.com這里主要介紹下c++中的lambda表達式。
lambda表達式又被稱為匿名函數,當我們在程序的某一處使用一個簡單的函數,并且只在此處或者此作用域中使用一次或者幾次,這時可以考慮使用lambda表達式。其基本語法如下:
lambda表達式的返回類型必須是尾置的。其中,參數列表和返回類型(返回類型忽略時,lambda可以根據其函數體中返回類型進行推斷)可以忽略。lambda表達式不能有默認參數,因此形參和實參的數量必須一致且一 一對應。lambda表達式內部是無法使用其所在函數中的(非static)變量的,但有時必須使用,就必須使用捕獲列表來捕獲其所在作用域中的參數(注意:只能捕獲lambda所在作用域中的參數,函數外的變量可以直接使用。例如:我們在lambda表達式函數體中使用的cout,可以直接使用)。根據捕獲參數的方式,可以分為:值捕獲,引用捕獲,隱式捕獲和表達式捕獲。
捕獲列表的形式如下:
1 值捕獲
通過值捕獲的變量會被拷貝到lambda產生的類對象中,因此這種捕獲方式必須為每個值捕獲的變量建立對應的數據成員。同時創建構造函數,令其捕獲的變量初始化數據成員。值捕獲的對象必須是可拷貝的。但其捕獲的值是在lambda表達式對象創建時拷貝,而不是運行時拷貝:
//按值捕獲int t=10;auto ff=[t]() mutable { //注意:這里是lambda函數的聲明return ++t; //這里生成了一個_t的變量};auto ff2=[t]() mutable { //注意:這里是lambda函數的聲明return ++t; //這里生成了一個_t的變量};cout<<ff()<<endl; //這里調用lambda表達式cout<<ff2()<<endl;cout<<ff()<<endl;cout<<ff2()<<endl;cout<<t<<endl;/*運行結果 11 11 12 12 10 */上述ff和ff2其本質是編譯器生成的未命名類的未命名對象。由于是值捕獲,因此++t只在lambda函數作用域有效,捕獲t后實際在內部生成了一個_t的成員變量。lambda表達式內部對t的操作實際上是對_t這個變量的操作,并不影響其外部的t的值。
默認我們捕獲到lambda表達式中的值是const的,是不能修改的,mutable關鍵字可允許我們對捕獲到的值進行修改。因此,在上述代碼中,++t的時候必須加上mutable。上述lambda表達式等價于:
class cfun{ public:fun(t):_t(t){};bool operator(){ //由于這里使用了mutable關鍵字,因此是這樣的_t++;}/*當沒有mutable關鍵字時,函數調用運算符重載如下形式:bool operator() const{/* 對_t的一些操作 */}*/private:int _t; };可變lambda
這里 再簡單介紹下mutable的使用。默認情況下,lambda不會改變一個值被拷貝的變量。如果希望改變被捕獲的變量,就必須使用mutable。因此,可變lambda可以省略參數列表。
void fun(){size_t v1=42;auto f=[v1] () mutable { return ++v1; };v1=0;auto j=f(); //j=43 }一個引用捕獲的變量是否可以修改依賴于引用指向一個const對象還是非const對象。
void fun(){size_t v1=42; //局部變量//v1是一個非const的變量引用,可以使用f來改變其值。auto f=[&v1] { return ++v1; };v1=0;auto j=f(); //j=1 }2 引用捕獲
當lambda用引用捕獲變量時,由程序負責確保lambda執行時引用所引的對象確實存在。因此,編譯器可以直接使用該引用而無需在lambda產生的類中將其存儲為數據成員。
//引用捕獲 int t=10; auto ff=[&t]() {cout<<t<<endl;t=13; }; t=11; ff(); cout<<t<<endl;//輸出結果 /* 11 13 */可以看到,引用捕獲的方式跟一般的引用形參函數類似,可以改變傳入的參數值。
引用捕獲是必要的,例如:
for_each(words.begin(),words.end(), [&os, c] (const string & s) { os<<s<<c; });因為ostream對象os是不能拷貝的。
當然,我們也可以從一個函數中返回一個lambda,但是lambda在返回后,函數中的變量會被銷毀,因此,lambda的捕獲列表中不能包含函數中變量的引用。
3 隱式捕獲
以上兩種方式都是顯式捕獲方式,這里介紹下隱式捕獲。編譯器可以根據lambda函數體中的代碼推斷我們會使用那些變量。其中&告訴編譯器引用捕獲方式,=采用值捕獲方式。
int a=1,b=2; [a,b](){cout<<a<<"+"<<b<<endl; }();//引用捕獲 [&](){cout<<a<<"+"<<b<<endl; }();//值捕獲 [=](){cout<<a<<"+"<<b<<endl; }();//混合使用 int c=3,d=4; [=,&c,&d](){cout<<a<<"+"<<b<<endl;cout<<c++<<"+"<<++d<<endl; }();當我們使用混合方式時,捕獲列表的第一個元素必須是&或者=。此符號指定了默認捕獲方式。顯式捕獲必須使用與隱式捕獲不同的方式。即,當默認捕獲方式是值捕獲時,顯式捕獲方式命名變量必須采用引用捕獲方式。
4 指定lambda返回類型
如果lambda函數體中包含了除return的任何語句,編譯器默認此lambda返回void。例如:
void fun(){auto ff4=[](int i){if(i<0)return -i;elsereturn i;};cout<<ff4(-100)<<endl; }此時會出現編譯錯誤,正確的寫法如下:
void fun(){auto ff4=[](int i)->int{if(i<0)return -i;elsereturn i;};cout<<ff4(-100)<<endl; }以下是一個lambda的使用例子:
void fun(){vector<int> vec={1,2,3,4,5};for(int i=0;i<vec.size();i++){if(vec[i]%2==0)cout<<vec[i]<<"是偶數"<<endl;elsecout<<vec[i]<<"是奇數"<<endl;}//高階寫法for_each(vec.begin(),vec.end(),[](int n){if(n%2==0)cout<<n<<"是偶數"<<endl;elsecout<<n<<"是奇數"<<endl;}); }總結
以上是生活随笔為你收集整理的必须使用列别名命名此表达式_lambda表达式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 02 字斟句酌
- 下一篇: 微信小程序API~地理位置locatio