c++中lambda表達式的用法

1.基礎用法

c++11提供了對匿名函數的支持,稱爲Lambda函數(也叫Lambda表達式)。Lambda表達式把函數看作對象。Lambda表達式可以像對象一樣使用,比如將它們賦給變量和作爲參數傳遞,還可以像函數一樣對其求值。Lambda表達式本質上與函數聲明非常類似。Lambda表達式具體形式如下:
[capture](parameters)mutable->return-type{}
1.[capture]:捕捉列表,總是出現在Lambda函數的開始出,實際上,[]是Lambda引出符。編譯器根據該引出符判斷接下來的代碼是否是Lambda函數。捕捉列表能夠捕捉上下文中的變量以供Lambda函數使用;
2.(parameters):參數列表。與普通函數的參數列表一致。如果不需要參數傳遞,則可以連同括號“()”一起省略;

3.mutable:mutable修飾符。默認情況下,Lambda函數總是一個const函數,mutable可以取消其常量性。在使用該修飾符時,參數列表不可省略(即使參數爲空);

4.->return-type:返回類型。用追蹤返回類型形式聲明函數的返回類型。我們可以在不需要返回值的時候也可以連同符號”->”一起省略。此外,在返回類型明確的情況下,也可以省略該部分,讓編譯器對返回類型進行推導;

5.{statement}:函數體。內容與普通函數一樣,不過除了可以使用參數之外,還可以使用所有捕獲的變量。

與普通函數最大的區別是,除了可以使用參數以外,Lambda函數還可以通過捕獲列表訪問一些上下文中的數據。具體地,捕捉列表描述了上下文中哪些數據可以被Lambda使用,以及使用方式(以值傳遞的方式或引用傳遞的方式)。語法上,在“[]”包括起來的是捕捉列表,捕捉列表由多個捕捉項組成,並以逗號分隔。捕捉列表有以下幾種形式:

1.[var]表示值傳遞方式捕捉變量var;
2.[=]表示值傳遞方式捕捉所有父作用域的變量(包括this);
3.[&var]表示引用傳遞捕捉變量var;
4.[&]表示引用傳遞方式捕捉所有父作用域的變量(包括this);
5.[this]表示值傳遞方式捕捉當前的this指針。

上面提到了一個父作用域,也就是包含Lambda函數的語句塊,說通俗點就是包含Lambda的“{}”代碼塊。上面的捕捉列表還可以進行組合,例如:

1.[=,&a,&b]表示以引用傳遞的方式捕捉變量a和b,以值傳遞方式捕捉其它所有變量;
2.[&,a,this]表示以值傳遞的方式捕捉變量a和this,引用傳遞方式捕捉其它所有變量。

不過值得注意的是,捕捉列表不允許變量重複傳遞。下面一些例子就是典型的重複,會導致編譯時期的錯誤。例如:

3.[=,a]這裏已經以值傳遞方式捕捉了所有變量,但是重複捕捉a了,會報錯的;
4.[&,&this]這裏&已經以引用傳遞方式捕捉了所有變量,再捕捉this也是一種重複。

2.兩點注意

實例一:

#include <iostream>
using namespace std;

int main(){
    int a = 10;
    auto func1 = [=]{return a + 1;};
    auto func2 = [&]{return a + 1;};
    cout << func1() << endl;
    cout << func2() << endl;
    a++;
    cout << func1() << endl;
    cout << func2() << endl;
    return 0;
}

輸出結果爲:

11
11
11
12

爲什麼第三個結果是11,而第四個結果是12呢?這是因爲在func1表達式中,a被視爲一個常量,一旦初始化後不會再改變,可以認爲是在表達式中copy了一個跟a同名的const變量,而在func2表達式中,a仍然在使用父作用域中的值,所以使用lambda函數的時候,如果需要捕捉的值成爲Lambda函數的常量,我們通常會使用按值傳遞的方式捕捉;相反的,如果需要捕捉的值成爲Lambda函數運行時的變量,則應該採用按引用方式進行捕捉。
實例二:

#include <iostream>
using namespace std;

int main(){
    int a = 10;
    cout << [=]{return ++a;}() << endl;
    cout << [&]{return ++a;}() << endl;
    return 0;
}

編譯會報錯,說a是隻讀的,換成下面的代碼就可以了。

#include <iostream>
using namespace std;

int main(){
    int a = 10;
    cout << [=]()mutable{return ++a;}() << endl;
    cout << [&]{return ++a;}() << endl;
    return 0;
}

這是因爲默認情況下,Lambda函數總是一個const函數,mutable可以取消其常量性。按照規定,一個const的成員函數是不能在函數體內修改非靜態成員變量的值。

總結:捕捉器這一塊,如果使用按值傳遞,就相當於在表達式定義那一刻,表達式內部就copy了一份變量,而且值永遠不變,即使表達式被多個地方調用,外部變量的變化不影響內部,如果想在表達式內部改變變量的值,需要加mutable關鍵字;如果是按引用傳遞,那麼表達式使用的值就是外部的變量的值,外部的變量值改變,那麼表達式內部得到的值也會改變,表達式內部也可以改變該變量,而且不需要加mutable關鍵字。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章