C++ lambda表達式 (一)


為什么要lambda函數

匿名函數是許多編程語言都支持的概念,有函數體,沒有函數名。1958年,lisp首先采用匿名函數,匿名函數最常用的是作為回調函數的值。正因為有這樣的需求,c++引入了lambda 函數,你可以在你的源碼中內聯一個lambda函數,這就使得創建快速的,一次性的函數變得簡單了。例如,你可以把lambda函數可在參數中傳遞給std::sort函數。

#include "stdafx.h"
#include <algorithm>   //標准模板庫算法庫
#include <cmath>       //數學庫
#include <iostream>
using namespace std;

//絕對值排序
void abssort(float* x, unsigned n) 
{
    //模板庫排序函數
    std::sort(x, x + n,
        // Lambda 開始位置
        [](float a, float b) 
        {
            return (std::abs(a) < std::abs(b));
        } // lambda表達式結束
    );
}

int _tmain(int argc, _TCHAR* argv[])
{
    float a[5] = { 2.1f, 3.5f, 4.0f, 5.2f, 3.3f };
    abssort(a, 5);
    for (auto& x : a)
    {
        cout << x << endl;
    }
    system("pause");
    return 0;
}

 

lambda函數的語法

基本形式如下:

 

[capture](parameters)->return-type {body}
  • []叫做捕獲說明符,表示一個lambda表達式的開始。接下來是參數列表,即這個匿名的lambda函數的參數。
  • parameters,普通參數列表
  • ->return-type表示返回類型,如果沒有返回類型,則可以省略這部分。這涉及到c++11的另一特性,參見自動類型推導,最后就是函數體部分。

  • capture clause(捕獲)
  • lambda-parameter-declaration-list (變量列表)
  • mutable-specification (捕獲的變量可否修改)
  • exception-specification (異常設定)
  • lambda-return-type-clause (返回類型)
  • compound-statement (函數體)

外部變量的捕獲規則

默認情況下,即捕獲字段為 [] 時,lambda表達式是不能訪問任何外部變量的,即表達式的函數體內無法訪問當前作用域下的變量。

如果要設定表達式能夠訪問外部變量,可以在 [] 內寫入 & 或者 = 加上變量名,其中 & 表示按引用訪問,= 表示按值訪問,變量之間用逗號分隔,比如 [=factor, &total] 表示按值訪問變量 factor,而按引用訪問 total。

不加變量名時表示設置默認捕獲字段,外部變量將按照默認字段獲取,后面在書寫變量名時不加符號表示按默認字段設置,比如下面三條字段都是同一含義:

[&total, factor]
[&, factor]
[=, &total]

#include <functional>
#include <iostream>
using namespace std;int _tmain(int argc, _TCHAR* argv[])
{
    //lambd函數對象
    auto fl = [](int x, int y){return x + y; };
    cout << fl(2, 3) << endl;

    function<int(int, int)>f2 = [](int x, int y){return x + y; };
    cout << f2(3, 4) << endl;
    system("pause");
    return 0;
}

不能訪問任何局部變量

如何傳進去局部變量

int test = 100;
    //lambd函數對象捕獲局部變量
    auto fl = [test](int x, int y){return test +x + y; };
    cout << fl(2, 3) << endl;

默認訪問所有局部變量

int test = 100;
    //lambd函數對象捕獲所有局部變量
    auto fl = [=](int x, int y){return test +x + y; };
    cout << fl(2, 3) << endl;

 

在C++11中這一部分被成為捕獲外部變量

捕獲外部變量

[captures] (params) mutable-> type{...} //lambda 表達式的完整形式

在 lambda 表達式引出操作符[ ]里的“captures”稱為“捕獲列表”,可以捕獲表達式外部作用域的變量,在函數體內部直接使用,這是與普通函數或函數對象最大的不同(C++里的包閉必須顯示指定捕獲,而lua語言里的則是默認直接捕獲所有外部變量。)

捕獲列表里可以有多個捕獲選項,以逗號分隔,使用了略微“新奇”的語法,規則如下

  • [ ]        :無捕獲,函數體內不能訪問任何外部變量 
  • [ =]      :以值(拷貝)的方式捕獲所有外部變量,函數體內可以訪問,但是不能修改。
  • [ &]      :以引用的方式捕獲所有外部變量,函數體內可以訪問並修改(需要當心無效的引用);
  • [ var]   :以值(拷貝)的方式捕獲某個外部變量,函數體可以訪問但不能修改。
  • [ &var] :以引用的方式獲取某個外部變量,函數體可以訪問並修改
  • [ this]   :捕獲this指針,可以訪問類的成員變量和函數,
  • [ =,&var] :引用捕獲變量var,其他外部變量使用值捕獲。
  • [ &,var]:只捕獲變量var,其他外部變量使用引用捕獲。

下面代碼示范了這些捕獲列表的用法:、

int x = 0,y=0;
  • auto f1 = [=](){ return x; };                       //以值方式捕獲使用變量,不能修改
  • auto f2 = [&](){ return ++x; };                  //以引用方式捕獲所有變量,可以修改,但要當心引用無效
  • auto f3 = [x](){ return x; };                      //以值方式捕獲x,不能修改
  • auto f4 = [x,&y](){ y += x; };                    //以值方式捕獲x,以引用方式捕獲y,y可以修改
  • auto f5 = [&,y](){ x += y;};                      //以引用方式捕獲y之外所有變量,y不能修改
  • auto f6 = [&](){ y += ++x;};                    //以引用方式捕獲所有變量,可以修改
  • auto f7 = [](){ return x ;};                      //無捕獲,不能使用外部變量,編譯錯誤

值得注意的是變化的捕獲發生在了lambda表達式的聲明之時,如果使用值方式捕獲,即使之后變量的值發生變化,lambda表達式也不會感知,仍然使用最初的值。如果想要使用外部變量的最新值就必須使用引用的捕獲方式,但也需要當心變量的生命周期,防止引用失效。

剛才的lambda表達式運行結果是:

  • f1();                     //以值方式捕獲,x,y不發生變化
  • f2();                    //函數內部x值為0,之后變為1,y沒有被修改,值仍然是0;
  • f3();                    //函數內部x值仍然為0,即f3()==0;
  • f4();                    //x,y均是0,運算后y仍然是0;
  • f5();                    //y是0;引用捕獲的x是1,運算后x仍然為1;
  • f6();                   //x,y均引用捕獲,運算后x,y均是2


免責聲明!

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



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