C++ lambda表達式 (一)

爲何要lambda函數

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

#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函數的語法

基本形式以下:c++

 

[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]this

#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;
}

不能訪問任何局部變量lua

如何傳進去局部變量spa

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

默認訪問全部局部變量3d

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
相關文章
相關標籤/搜索