C++基础知识6:lambda表达式

C++基础知识6:lambda表达式

  • 本文目的
  • 前置与关联知识点
  • lambda表达式简介
  • lambda表达式的形式
  • lambda表达式使用的意义
  • lamda代替函数符或者函数指针像算法传递可调用对象(谓词)
    • 1. 向STL算法传递二元谓词
    • 2. 作为模板参数
  • lamda表达式使用捕获列表
  • lamda表达式捕获
    • 1. 值捕获
    • 2. 引用捕获
    • 3. 隐式捕获
    • 4. 可变lambda
  • 指定lambda返回类型
  • 参考文献

本文目的

简单的介绍一下lambda表达式在不同地方使用的作用,在STL算法函数,在类里面的使用,以及相关应用举例

前置与关联知识点

  1. C++基础知识1:仿函数function objects
  2. 二元谓词/二元判断式,可以参考数据结构与函数使用2:排序函数sort

lambda表达式简介

定义lambda的时候,编译器生成一个与lambda对应的新的未命名的类类型
参考Primer C++(第五版) 10.3.2 lambda 表达式 p346

到目前为止,我们使用过的仅有的两种可调用对象是函数和函数指针。
还有其他两种可调用对象:重载了函数调用运算符的类,以及lambda表达式(lambda expression)。

lambda表达式的形式

[capture list](parameter list)->return type {function body}
//[捕获列表](参数列表)->返回值 {函数主题}
//一个典型的例子
  1. 一般返回值根据函数体的代码推断返回类型,所以可以忽略
    auto cmp = [](const int & lhs, const int & rhs){return lhs<rhs};
    
  2. 没有参数的时候也可以忽略参数列表,如下
    int cap1;
    auto cmp = [cap1]{return cap1};
    

lambda表达式使用的意义

  • 在C++中引入 lambda是一个类似于函数的表达式,用于代替函数指针或函数符。

好处

  1. 距离:和函数指针相比,可以函数内进行定义,让定义位于使用的地方附近,便于查看和修改。
  2. 简洁:和函数符相比,实现的代码更加简单。
  3. 效率:取决于编译器内联哪些东西(不是很懂)。
  4. 功能:通过捕获的方式访问作用域内的任何动态变量。

lamda代替函数符或者函数指针像算法传递可调用对象(谓词)

1. 向STL算法传递二元谓词

举个例子,以sort函数为例子

//对nums进行降序排序
vector<int> nums = {1,2,3,1,3,5,2,1}
//方式1
auto cmp = [](const int & lhs, const int & rhs){return lhs>rhs};
sort(nums.begin(),nums.end(),cmp);
//方式2
sort(nums.begin(),nums.end(),[](const int & lhs, const int & rhs){return lhs<rhs});

2. 作为模板参数

举个例子,以priority_queue为例子

//创建一个大根堆
vector<int> nums = {1,2,3,1,3,5,2,1}
//1. 定义lambda表达式
auto cmp = [](const int & lhs, const int & rhs){return lhs>rhs};
//2. 定义priority_queue,
priority_queue<int,vector<int>, decltype(cmp)> pqueue;
sort(nums.begin(),nums.end(),[](const int & lhs, const int & rhs){return lhs<rhs});

//3. 填充元素
for(auto num:nums)
	pqueue.push(num);

lamda表达式使用捕获列表

参考Primer C++(第五版) 10.3.2lambda表达式 p345

虽然一个 lambda可以出现在一个函数中,使用其局部变量,但它只能使用那些明确指明的变量。一个lambda通过将局部变量包含在其捕获列表中来指出将会使用这些变量。捕获列表指引 lambda在其内部包含访问局部变量所需的信息。

参考Primer C++(第五版) 10.3.2lambda表达式 p348

一个lambda只能使用他捕获列表中存在的他所在函数中的局部变量。

这里举一个最简单的例子,用于比较一个输入的形参是否比judge值大。

void myfun()
{
	int judge = 100;
	auto judge_fun = [judge](int aim){return judge < aim};
	//	auto judge_fun = [](int aim){return judge < aim};这一行不捕获直接使用judge就会报错
	cout<<judge_fun();
}

lamda表达式捕获

参考Primer C++(第五版) 10.3.3lambda捕获和返回 p352
C++基础知识6:lambda表达式_第1张图片

类似于参数传递,变量的捕获的方式可以是值或者引用

1. 值捕获

值捕获的前提是变量可以拷贝,而且是在lambda创建的时候拷贝,而非调用时

void fcn1()
{
	int v1 = 42;
	auto f = [v1]{ return v1 };
	v1 = 0;
	auto j = f(); //j = 42而不是0,lambda创建后修改v1不会改变捕获的值
}

2. 引用捕获

需要保证运行的时候引用捕获的值依然存在

void fcn1()
{
	int v1 = 42;
	auto f = [&v1]{ return v1 };
	v1 = 0;
	auto j = f(); //j = 42而不是0,lambda创建后修改v1不会改变捕获的值
}
  • 存在一些无法拷贝的对象的时候回去使用引用(如ostream对象只能接收普通引用)。

3. 隐式捕获

非显式,让编译器推断捕获列表,可以看上面的表格的写法。

4. 可变lambda

  • 默认情况下lambda对于被拷贝的变量也不会改变这个值,如果需要改变需要+mutable
    void fcn1()
    {
    	int v1 = 42;
    	auto f = [v1]{ return ++v1 };
    	v1 = 0;auto j = f(); //j = 43
    }
    

引用捕获的话同一般的函数。

指定lambda返回类型

有时候编译器无法推断返回类型,比如有多个等价的if语句里面的return。

auto fcn = [](int i){if (i < 0) return -i; else return i;};//会报错
auto fcn = [](int i)->int{if (i < 0) return -i; else return i;};//不会报错

参考文献

Primer C++(第五版) 10.3.2lambda表达式 p345
Primer C++(第五版) 10.3.2 lambda 表达式 p346
Primer C++(第五版) 10.3.2lambda表达式 p348
Primer C++(第五版) 10.3.3lambda捕获和返回 p352

你可能感兴趣的:(C++基础知识,c++,开发语言)