c++11新特性--lambda表達式


從c++11開始,可以使用lambda表達式,介紹一下使用方法以及事項, 你可以把它當作一個無名重載了operator()運算符的類對象。


知識背景

尾置返回類型

C++11標准中,引入定義函數時的一種新的方法,使用尾置返回類型.這種形式對於返回類型比較復雜的情況很有效。
通常情況下,我們定義或聲明一個函數時,是這樣的:

int add(int a, int b);

尾置返回類型的定義是這樣的:

auto add(int a, int b) -> int;

當我們定義一個返回指向10個元素的int數組指針的函數時,按正常形式是這樣的:

int (*func())[10];

當我們使用尾置返回類型時,它是這樣的:

auto func() -> int(*)[10];

lambda表達式

定義格式

lambda采用尾置返回類型,它完整的const聲明形式為: [] (參數列表) -> 返回值類型 {函數體} , const是指在捕獲列表內通過值捕獲的參數在lambda內部是不可以改變的。

int func(vector<int> array)
{
    auto func = [](int a, int b) ->bool { return a < b;}
    std::sort(array.begin(), array.end(), func);
}

void sample ()
{
    int a = 10;
    auto func1 = [a] () {++a;};             // 編譯錯誤, 變量a的類型在lambda體內為const的。
    auto func2 = [a] () mutable {++a;};     // 沒有問題
    auto func3 = [&a] () {++a;};            // 沒有問題
}

參數列表與返回值類型可以根據是否需要,進行省略掉, 規則如下:

  • 當定義的lambda表達式的參數為空時,可以省略掉: [] -> 返回值類型 {函數體 }
  • 當定義的lambda表達式的函數體僅有一條 return **** 的語句時,返回值類型也可以省略,編譯器會根據返回值的類型自動推斷。 但是如果函數體是多條語句而你省略了返回值類型,編譯器認為返回類型是void. 例如下面的lambda表達式的返回值類型默認為void, 一看就不對啊,所以此時你別省略返回值類型:
    auto Addfunc = [] (int a, int b) { int c = a + b; return c;}
    
  • 捕獲列表與函數體任何時候都不可以省略。

捕獲列表

捕獲列表只需要捕獲lambda所在作用域內常規的局部變量,對於非局部變量以及局部的靜態變量不需要捕獲,可以直接使用。 例如:

int g_value = 100;
void sample()
{
    int value1 = 10;
    static value2 = 100;
    auto func = [value1]() {cout << g_value << value1 << value2; };
}

值捕獲

方式一: 使用 [=] 隱式捕獲lambda內所有使用到的變量的值。
方式二: 使用 [val1, val2, val3, ...] 顯示捕獲lambda內使用到的變量的值。

void sample()
{
    int value1 = 10;
    int value2 = 10;
    int value3 = 10;
    auto func1 = [=] () -> int {return value1 + value2 + value3;};
    auto func2 = [value1, value2, value3] () {value1 + value2 + value3;};
}

引用捕獲

方式一: 使用 [&] 隱式捕獲lambda內所有使用到的變量的值。
方式二: 使用 [&val1, &val2, &val3, ...] 顯示捕獲lambda內使用到的變量的值。

void sample()
{
    int value1 = 10;
    int value2 = 10;
    int value3 = 10;
    auto func1 = [&] () -> int {return value1 + value2 + value3;};
    auto func2 = [&value1, &value2, &value3] () {return value1 + value2 + value3;};
}

混合捕獲

方式一: 使用 [val1, &val2, val3, ...] 隨意的組合值捕獲和引用捕獲來獲取lambda內使用到的變量的值。
方式二: 使用 [=, &val1, &val2, ...] 表示除了手動指出來的變量通過引用捕獲之外,其它的變量都是通過值進行捕獲。
方式三: 使用 [&, val1, val2, ...] 表示除了手動指出來的變量通過值捕獲之外,其它的變量都是通過引用進行捕獲。

void sample()
{
    int value1 = 10;
    int value2 = 10;
    int value3 = 10;
    auto func1 = [value1, &value2, value3] () {return value1 + value2 + value3;};
    auto func2 = [=, &value2] () {return value1 + value2 + value3;};
    auto func3 = [&, value3] () {return value1 + value2 + value3;};
}

注意事項:

  1. 當lambda表達式定義在類內的成員函數時,如果在lambda表達式內部要訪問類的成員函數或成員變量(無論public/protected/private)時,要么顯示捕獲this指針,要么通過[=]或[&]進行隱式捕獲。
  2. 使用引用捕獲時,特別注意這些參數實體的生存期,保證調用lambda時這些實體是有意義的,避免懸垂引用的產生。

使用mutable關鍵字修飾的lambda

默認的lamba的聲明方式是const聲明,通過值獲取的參數在lambda內是無法修改的,如果要改變該值,在參數列表后面加上mutable關鍵字。

void sample ()
{
    int a = 10;
    auto func1 = [a] () {++a;};             // 編譯錯誤
    auto func2 = [a] () mutable {++a;};     // 沒有問題
}

lambda表達式本身是純右值表達式(你不可能對它的結果取地址的), 它的類型獨有的無名非聯合非聚合類類型,被稱為閉包類型(closure type), 它可以有以下兩個成員函數:

ret-type operator() (參數列表) const {函數體}     // 未使用關鍵字mutable時,默認情況
ret-type operator() (參數列表) {函數體}           // 使用了mutable 關鍵字時

友情提示:

mutable 關鍵字只對值捕獲參數有影響,對引用捕獲的參數無影響。原因是:引用參數能否修改為參數本身是否為const類型決定。即使一個類的成員函數有 const 聲明(該const就是修改this指針的),照樣可以通過該成員函數修改一個引用類型的成員變量,例如:

class Test {
public:
    Test(int& value) : a(value) {}
    void Increase() const {++a;}
private:
    int& a;
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM