C++ lambda表达式

一、lambda概述

要介绍什么时lambda表达式,首先要认识什么是可调用对象。可调用对象的定义是:对于一个对象或一个表达式,如果可以对其使用调用运算符“()”,则称为可调用的。因此我们所熟知的可调用对象就是函数函数指针,另外还有两种可调用对象就是重载了函数调用运算符的类lambda表达式。根据它的本质,我们可以把它看做成是一个没有名字的函数,也可以称为匿名函数。


二、lambda语法分析

语法:[capture list]    (parameter list)    ->    retur type    { function body }

capture list:为捕获列表,它的功能是捕获lambda所在函数中的局部变量,以便在lambda函数体中使用

parameter list:是参数列表,与普通函数一样

return type:返回类型,与普通函数一样

function body:函数体,与普通函数一样

其中参数列表和返回类型是可以忽略的,但捕获列表和函数体一定要有

 

三、lambda应用场景及用法

1. 基本用法

以下例子忽略了参数列表和返回类型

auto f = [] { cout << "my lambda function" << endl; };
f();

auto f2 = [] { return 100; };
cout << f2() << endl;

/*
output:
my lambda function
100
*/

2. 结合STL使用

当我们使用泛型算法时,有些算法的第三个参数是可以接受一个谓词,谓词是一个可调用的表达式,所以我们可以传递一个函数或者一个lambda表达式,例如以下用法,功能是打印vecrot中元素的值

vector<int> vec= {1,2,3,4,5};
for_each(vec.begin(),vec.end(),
          [] (const int &val) { cout << val << endl;});
/*
output:
1
2
3
4
5
*/

3.捕获列表用法

3.1捕获列表的变量是lambda对象的数据成员

要介绍捕获列表,首先就要介绍以下lambda与函数不一样的地方了,当定义一个lambda时,编译器就会生成一个与lambda对应的类对象,而捕获列表中的变量就是lambda生成类中的数据成员,因此在创建lambda对象时,捕获列表的变量就会被初次化,这就说明捕获列表的变量的值不是在调用时确定的,而是在定义的时候就已经确定了。

3.2 值捕获

捕获列表的作用是使用定义lambda函数中的局部变量,值捕获就对捕获的变量进行拷贝,拷贝完之后生成的副本其值与局部变量的值已经没有关系了

int val = 10;
auto f1 = [val] { return val; };
val = 20;
cout << f1() << endl;

/*
output:
10
*/

如果没有捕获变量就使用该局部变量,编译器会报错

int val = 10;
int v = 2;
auto f1 = [val] { return val + v; };

/*
complie error:
error: 'v' is not captured|
*/

 

3.3 引用捕获

引用捕获顾名思义就是以引用的方式去捕获,不是通过拷贝而是通过绑定的方式,与拷贝的捕获变量不同,对于引用捕获当调用lambda时,其捕获值会与绑定的值关联一起,有些对象无法拷贝,就需要使用引用捕获,例如ostream对象

int val = 10;
auto f1 = [&val] { return val; };
val = 20;
cout << f1() << endl;

ostream &os = cout;
auto f2 = [&os] { os << "ref capture lambda func" <<endl; };
f2();

/*
output:
20
ref capture lambda func
*/


3.4 显式捕获与隐式捕获

显示捕获是指我们在捕获列表中列出想要捕获的局部变量名,而隐式捕获就是不列出变量名,让编译器来推断我们要使用哪些变量,隐式的值捕获是[=],隐式的引用捕获是[&]

int val = 10;
int val2 = 20;
auto f1 = [=] { return val; };        //隐式捕获,其捕获方式是值捕获
auto f2 = [&] { return val2; };       //隐式捕获,其捕获方式是引用捕获
cout << f1() << endl;
cout << f2() << endl;

/*
output:
10
20
*/

我们也可以将显示和隐式混合使用,但需要注意的是当混合使用时,捕获列表的第一个元素必须是&或=,即指定隐式在前显式在后

int val = 10;
int val2 = 20;
int val3 = 30;
auto f1 = [=,&val3] { return val + val3; };
auto f2 = [&,val3] { return val2 + val3; };
cout << f1() << endl;
cout << f2() << endl;

/*
output:
40
50
*/


3.5 mutable关键字可使捕获的变量值改变

一般情况下,当我们使用值捕获后修改了lambda函数体中的捕获变量,编译器是会报错的

int val = 10;
auto f1 = [val] { return ++val; };

/*
error: increment of read-only variable 'val'
*/

因此如果我们想修改其值,可以用mutable关键字,此时参数列表不能忽略,对于引用捕获,如果捕获的变量不是const的话是可以修改的

int val = 10;
int val2 = 20;
auto f1 = [val] () mutable{ return ++val; };        //不能忽略参数列表
auto f2 = [&val2] { return ++val2; };
cout << f1() << endl;
cout << f2() << endl;

/*
output:
11
21
*/


3.6 结合只接受一元谓词的STL使用

有些泛型算法只接受一元谓词(所谓一元谓词就是意味这可调用对象只接受一个参数),当是我们又想传入两个参数给lambda使用,这时就可以捕获列表就起作用了,以下我们用find_if为例子,此代码功能是找出vector中长度大于等于4的字符串

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

#define FIND_NAME_SIZE  4

using namespace std;

int main()
{
    vector<string> v_str= {"TOM","TONY","JACKY","DAVID"};
    size_t sz = FIND_NAME_SIZE;
    
    auto first_name_it= find_if(v_str.begin(),v_str.end(),
              [sz] (const string &a) { return a.size() >= sz;});

    cout << *first_name_it << endl;

    return 0;
}

/*
TONY
*/

4. 指定lambda的返回类型

返回类型是可以忽略的,那什么时候需要我们指定返回类型呢?当函数体包含return之外的语句,此时编译器假定此lambda返回void,所以需要用尾置返回指定返回类型

int val = 10;
auto f1 = [] (int val) -> int { if(val > 0) return -val; else return val; };
cout << f1(val) << endl;

四、lambda使用注意

不能有默认参数
       lambda函数体中要使用非static的局部变量,捕获列表必须要有
       如果有一个函数返回lambda对象,则此lambda不能包含引用捕获(原因与不能返回一个局部变量的引用类似)
       当混合使用隐式捕获和显式捕获时,捕获列表的显式捕获变量必须使用与隐式捕获不同的方式

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值