作者:李春港
出处:https://www.cnblogs.com/lcgbk/p/14088462.html

一、前言

由于前段时间在阅读一些C++源码的时候发现了Lambda表达式,所以在此也记录下Lambda表达式的使用。

很早之前Lambda在很多高级语言中,就已经被广泛地使用了,在一个程序中Lambda表达式可以理解为是匿名函数。在C++中,到了C++11标准才引入了这个Lambda表达式,这是C++11最重要而且也是最常用的特性之一。

使用Lambda表达式,不需要额外地定义函数名,可以更直接编写程序,有比较好的可读性和可维护性;不需要另外声明和定义函数体,避免了程序代码的膨胀。

二、Lambda表达式格式说明

2.1 完整的Lambda表达式格式

[capture list] (params list) mutable exception-> return type { function body }

说明:

2.2 常见的Lambda表达式格式

2.3 lambda 表达式捕获列表

注意:

  • 如果是按值捕获,那么是否可以改变捕获的变量值,取决于mutable关键字。

三、示例

3.1 STL的sort函数参数使用Lambda

/*****************************************************************************
** Copyright © 2020 lcg. All rights reserved.
** File name: Lambda.cpp
** Description: 在STL的sort函数参数使用Lambda表达式
** Author: lcg
** Version: 1.0
** Date: 2020.12.04
*****************************************************************************/

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

bool cmp(int a, int b)
{
    return  a < b;
}

int main()
{
    vector<int> vec{ 3, 2, 5, 7, 3, 2 };
    vector<int> lbvec(vec);

    /**1、不使用Lambda表达式的写法**/
    sort(vec.begin(), vec.end(), cmp);
    cout << "predicate function:" << endl;
    for (int it : vec) // 此for循环写法也是在C++11才出现
        cout << it << ' ';
    cout << endl;

    /**2、使用Lambda表达式的写法**/
    sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; });
    cout << "lambda expression:" << endl;
    for (int it : lbvec)
        cout << it << ' ';
}

可以看到这种情况使用Lambda表达式可以使代码更加直观、简介,无需再定义 cmp(int a, int b) 函数。

3.2 有返回值的Lambda表达式

/** 1、标明返回类型**/
auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl;  /**输出: 2**/

/** 2、无标明返回类型**/
auto f = [](int a) { return a + 1; };
std::cout << f(1) << std::endl;  /**输出: 2**/

当没有标明返回类型的时候,系统会根据return回来的值来判断返回值的类型,auto会自动检索返回值的类型。

3.3 无参数Lambda表达式

auto f = []() { return 1; };
std::cout << f() << std::endl;  /**输出: 1**/

3.4 捕获外部变量的Lambda表达式

/*****************************************************************************
** Copyright © 2020 lcg. All rights reserved.
** File name: Lambda.cpp
** Description: 捕获外部变量的Lambda表达式
** Author: lcg
** Version: 1.0
** Date: 2020.12.04
*****************************************************************************/

#include <iostream>

class A
{
public:
    int i_ = 0;
    void func(int x, int y)
    {
        /* error,没有捕获外部变量*/
        auto x1 = []{ return i_; };

        /*OK,按值捕获所有外部变量,包括了this指针*/
        auto x2 = [=]{ return i_ + x + y; };

         /*OK,按引用捕获所有外部变量,包括了this指针*/
        auto x3 = [&]{ return i_ + x + y; };

        /*OK,捕获this指针,Lambda拥有和此类中普通函数一样的权限*/
        auto x4 = [this]{ return i_; };

        /*error,没有捕获x、y,因为x、y变量不属于this*/
        auto x5 = [this]{ return i_ + x + y; };

        /* OK,捕获this指针、x、y*/
        auto x6 = [this, x, y]{ return i_ + x + y; };

        /*OK,捕获this指针,并修改成员的值*/
        auto x7 = [this]{ return i_=7; };
        x7();
        std::cout<<i_<<std::endl;//输出7

        /*OK,捕获所有外部变量,默认捕获this指针,并修改成员的值*/
        auto x8 = [=]{ return i_=8; };
        x8();
        std::cout<<i_<<std::endl;//输出8

        /*error,因为i_不属于捕获范围的变量,所以无法按值捕获i_变量,可以通过捕获this指针来获取i_使用权*/
        auto x9 = [i_]{ return i_++; };

        /*error,原因同上*/
        auto x10 = [&i_]{ return i_++; };

        /*ok,按引用捕获所有变量,默认捕获this指针,并修改成员的值*/
        auto x11 = [&]{ return i_=11; };
        x11();
        std::cout<<i_<<std::endl;//输出11

        /*error,按值捕获x变量,并修改值*/
        auto x12 = [x]{ return x++; };

        /*ok,按值捕获x变量,并修改值*/
        auto x13 = [x]()mutable{ return x=13; };
        x13();
        std::cout<<x<<std::endl;//输出1

        /*ok,按引用捕获x变量,并修改值*/
        auto x14 = [&x]{ return x=14; };
        x14();
        std::cout<<x<<std::endl;//输出14
    }
};


int main(int argc, char *argv[])
{
    A l;
    l.func(1,2);

    int a = 0, b = 1;
    /*error,没有捕获外部变量*/
    auto f1 = []{ return a; };

    /*OK,捕获所有外部变量,改变a值*/
    auto f2 = [&]{ return a=2; };
    f2();
    std::cout<<a<<std::endl;//输出2

    /*OK,捕获所有外部变量,并返回a*/
    auto f3 = [=]{ return a; };

    /*error,a是以复制方式捕获的,无法修改*/
    auto f4 = [=]{ return a=4; };

    /*ok,a是以复制方式捕获的,修改a值*/
    auto f4 = [=]()mutable{ return a=4; };
    f4();
    std::cout<<a<<std::endl;//输出2

    /*error,没有捕获变量b*/
    auto f5 = [a]{ return a + b; };

    /*OK,捕获a和b的引用,并对b做自加运算*/
    auto f6 = [a, &b]{ return a + (b++); };

    /*OK,捕获所有外部变量和b的引用,并对b做自加运算*/
    auto f7 = [=, &b]{ return a + (b++); };

    return 0;
}

总结:

  • 当按值的方式获取外部变量时,是无法更改获取过来的值的,除非使用mutable关键字声明,就可以更改(更改的不是外部变量原本地址里的值,而是lambda函数体内的副本);
  • 当按引用的方式捕获外部变量时,lambda函数体内可以更改此值,更改的是外部变量原本地址里的值;
  • 当在类中,lambda表达式捕获this指针时,lambda函数体内可以直接改变该类中的变量,和类中的普通函数拥有一样的权限。
12-05 15:29