10

【C++】C++之Lambda表达式

 3 years ago
source link: http://www.cnblogs.com/lcgbk/p/14088462.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

作者:李春港

出处: https://www.cnblogs.com/lcgbk/p/14088462.html

目录

  • 二、Lambda表达式格式说明
    • 2.1 完整的Lambda表达式格式
    • 2.2 常见的Lambda表达式格式
    • 2.3 lambda 表达式捕获列表
    • 3.1 STL的sort函数参数使用Lambda
    • 3.2 有返回值的Lambda表达式
    • 3.3 无参数Lambda表达式
    • 3.4 捕获外部变量的Lambda表达式

一、前言

由于前段时间在阅读一些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 }

说明:

名称 解析 [capture list] 捕获列表:lambda 表达式可以通过捕获列表捕获一定范围内的变量。 (params list) 形参列表,用于传参(可以省略)。 mutable 用来说明是否可以修改按值捕获的变量(可以省略),如果需要修改按值捕获的变量,则需要添加。 exception 异常设定(可以省略)。 return type 返回类型 (可省略,如果省略则自动从函数体中判断返回类型,return后的值。如果没有则返回void)。 function body 函数体,即逻辑代码。

2.2 常见的Lambda表达式格式

编号 格式 特性 格式1 [capture list] (params list) -> return type {function body} 1、无法修改捕获列表中的变量值。 格式2 [capture list] (params list) {function body} 1、无法修改捕获列表中的变量值;2、返回类型由return返回的值类型确定,如果没有return语句,则返回类型为void。 格式3 [capture list] {function body} 1、无法修改捕获列表中的变量值;2、返回类型由return返回的值类型确定,如果没有return语句,则返回类型为void;3、不能传入参数,类似普通的无参函数。

2.3 lambda 表达式捕获列表

捕获形式 解析 [ ] 不捕获任何变量。 [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。 [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。 [=,&x] 按值捕获外部作用域中所有变量,并按引用捕获 x 变量。 [x] 按值捕获 x 变量,同时不捕获其他变量。 [this] 捕获当前类中的 this 指针,让 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函数体内可以直接改变该类中的变量,和类中的普通函数拥有一样的权限。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK