IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ Primer笔记——lambda表达式与bind函数 -> 正文阅读

[C++知识库]C++ Primer笔记——lambda表达式与bind函数

目录

一.lambda介绍

(一).总体介绍

(二).参数列表

(三).尾置返回

(四).捕捉列表

①值捕捉

②引用捕捉

③隐式捕捉

?(五).lambda与捕捉列表的本质

二. 参数绑定bind

(一).什么情况下会用bind

(二).使用方式

①参数列表?

②参数顺序

③引用类型参数


一.lambda介绍

(一).总体介绍

lambda是C++11所规定的一种新方法。一般用于泛型算法传递自定义函数。

其形式如下:

[ 捕捉列表 ]? ( 参数列表 )? ->返回值类型? { 函数体 }

lambda表达式会返回一个函数对象。

举个例子:

int a = 1, b = 3;
auto f = [](int i, int j)->int {return i + j; };
f(a, b);

等价于下列函数
int f(int i, int j)
{
    return i + j;
}

实际开发中,一般用于给算法传仿函数,比如:

vector<int> a = { 4,3,5,6,2,5,7,8,5,3 };
//按降序排序
sort(a.begin(), a.end(), [](const int i, const int j)->bool { return i > j; });

使用lambda表达式时,有几点需要注意:

1.捕捉列表可以为空

2.尾置返回类型和参数列表可以忽略,即[]{};

3.捕捉列表和函数体不能忽略。

(二).参数列表

使用参数列表时,按照普通函数的参数列表使用即可,唯一的不同是不能有缺省参数

因此,lambda的实参数量永远与形参保持一致。?

当然,参数可以是引用、指针类型,const修饰也没问题。

(三).尾置返回

与普通函数不同,lambda只能使用尾置返回类型,如果忽略尾置返回,那么编译器会自行推断返回类型,当然这个推断也是有条件的:

1.函数体没有return语句,自动推断为void类型,即无需返回。

2.只有一条return语句,根据return类型推断返回类型。

3.多条return语句,返回void(实际上在vs环境下也会根据return自行推到返回类型)

(四).捕捉列表

顾名思义,捕捉列表就是捕捉已存在的局部变量,因此使用捕捉列表必须捕捉那些已存在且可访问的对象。

同时,即便lambda在函数作用域内,如果没有进行捕捉或列表传参,那么也不能直接使用该变量。

尤其需要注意的是,捕捉列表只能捕捉临时变量,static、全局变量因为具有静态属性不能捕捉。

即声明周期不随函数作用域的变量不能捕捉,因为静态变量本来就可以直接使用自然不需要捕捉。

示例如下:

int i = 2, j = 4;
//捕捉i和j
auto f = [i, j] { return i < j; };
int i = 2;
static int j = 4;
auto f = [i, j] { return i < j; };//错误,j是静态变量

?当然捕捉也分三种情况:

①值捕捉

默认情况下,捕捉列表按照传值的方式捕捉变量,因此,传递给lambda的并不是变量本身,而是变量的一份拷贝,lambda的修改不会影响变量本身

②引用捕捉

在捕捉参数前加上&即引用捕捉。

在这种情况下,传递给lambda的即是变量本身,当在lambda函数体中进行修改时,变量本身也进行修改

引用捕捉的常用场景是那些拷贝代价很大的对象,或者不支持拷贝的对象,比如iostream对象就不支持拷贝,只能引用捕捉。

③隐式捕捉

除了可以显式捕捉变量的方式外,还可以使用=或&进行隐式捕捉。

使用=是值捕捉,使用&是引用捕捉。

int i = 0, j = 4;
//将i和j全部按值捕捉
auto f = [=] { return i < j; };
//将i和j全部按引用捕捉
auto f = [&] { return i < j; };

使用时,隐式捕捉可以和显式捕捉(值、引用捕捉)混合使用,但是有如下要求:

1.隐式捕捉必须在捕捉列表第一个位置。

2.定义的隐式捕捉方式即是指定默认捕捉方式,捕捉列表后续参数默认该方式捕捉。

3.后续显式捕捉的方式必须和隐式捕捉不同。

int i = 0, j = 4;
auto f = [=, &j] { return i < j; };//j按引用捕捉,其余全部按值捕捉
auto f = [&, j] { return i < j; };//j按值捕捉,其余全部按引用捕捉
auto f = [&, &j] { return i < j; };//错误
auto f = [=, j] { return i < j; };//错误

?(五).lambda与捕捉列表的本质

《C++ Primer》中指出,lambda本质是一个函数对象,或者是仿函数。默认情况下,只有一个operator()函数,没有默认构造、赋值、析构函数。

当我们使用lambda时,会生成一个未命名对象,使用时通过该对象调用重载的()完成函数功能。

大致如下:

auto f = [](const int& a, const int& b) { return a < b; };
//等价于
class XXXXX {
public:
	bool operator()(const int& a, const int& b) {
		return a < b;
	}
};

当使用捕捉列表时,情况分为两种:

1.按引用捕捉,编译器会将捕捉对象直接在类内使用,无需定义成员。

2.按值捕捉,因为有拷贝的发生,不能直接使用捕捉的对象。

此时,lambda类会定义一个带参的构造函数并在类内定义值捕捉的成员变量。构造函数使用值捕捉对象给成员变量赋值。

auto f = [i, j]{ return i < j; };//值捕捉
class XXXXX {
public:
	XXXXX(int a, int b)//生成构造函数
		:i(a), j(b)
	{}
	bool operator()(const int& a, const int& b) {
		return a < b;
	}
private:
    //值捕捉的变量的拷贝
	int i;
	int j;
};

lambda是否具有构造、拷贝构造、析构函数,要看具体捕捉成员类型而定。

二. 参数绑定bind

(一).什么情况下会用bind

以find_if函数举例,其传入的第三个是函数对象,且函数参数只能有一个

但加入我们需要两个参数呢,比如想通过比较来判断是否是要找的值呢?

//通过判断i是否大于j,来确定我们要找的值
bool judge(int i, int j) {
	return i > j;
}

vector<int> a = { 4,3,5,6,2,5,7,8,5,3 };
auto x = find_if(a.begin(), a.end(), judge);//错误,judge参数只能唯一

这种情况下,可以通过使用lambda表达式解决,将j传进捕捉列表。

vector<int> a = { 4,3,5,6,2,5,7,8,5,3 };
//lambda方式解决
int j = 7;
auto x = find_if(a.begin(), a.end(), [j](int i) {return i > j; });

但是,如果我们就想用judge函数代替lambda呢😂,使用bing参数绑定。

(二).使用方式

bind函数定义在头文件<functional>中。

使用形式为:

auto 返回对象 = bind(函数名, ...);

其中返回类型为_Binder类(vs环境下),但具体函数会有具体的类型,因为_Binder是类模板。

①参数列表?

...省略符所代表的就是函数参数列表,其中需要直接绑定给函数的在bind中给出,需要使用函数传参的使用占位符代替。

占位符在placeholders命名空间中,placeholders又在std中。

占位符使用时如下:

std::placeholders::_1 //1号占位符

以judge为例,使用bind函数方式如下:

int j = 7;
//bind返回值类型使用auto即可
//直接将j绑定到judge第二参数,第一参数需要函数传参
//生成x对象代替judge函数
auto x = bind(judge, std::placeholders::_1, j);
//此时x只需要一个参数即可
auto y = find_if(a.begin(), a.end(), x);

②参数顺序

占位符表示的参数,不受出现顺序限制。

比如如下函数:

bool judge(int i, int j, int z) {
	return i > j && i > z;
}
int a = 2, b = 3, c = 1;
auto x = bind(judge, std::placeholders::_1, a, std::placeholders::_2);
auto y = bind(judge, std::placeholders::_2, a, std::placeholders::_1);

当调用x和y时可以表示成下面情况:

//x
x(1, 2) == judge(1, a, 2);
//y
y(1, 2) == judge(2, a, 1);

?即_1占位符表示的永远是函数第一个需要传入的参数,与bind中位置无关。

但是bind中直接绑定的参数,与函数中对应位置绑定。

③引用类型参数

如果函数参数是引用类型呢,比如传递iostream对象(禁止拷贝)。

那么bind中需要绑定引用类型参数,就要使用标准库中的ref函数。

int a = 0;
//传a的引用
auto x = bind(judge, std::placeholders::_1, ref(a), std::placeholders::_2);

?参考书籍:

《C++ Primer》P349-P357、P507-P509

一个好的程序员应该是那种过单行线都要往两边看的人———Doug Linder


如有错误,敬请斧正?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 10:47:31  更:2022-12-25 10:51:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/22 19:39:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码