【C++11】:lambda表达式function包装器

news2024/9/28 15:31:31

目录

  • 前言
  • 一,可变参数模板
    • 1.1 简单认识
    • 1.2 STL容器中的empalce系列相关接口
  • 二,lambda表达式
    • 2.1 lambda表达式语法
    • 2.2 探索lambda底层
  • 三,包装器
    • 3.1 function包装器
    • 3.2 bind
  • 四,类的新功能
    • 4.1 默认成员函数
    • 4.2 关键字default
    • 4.3 关键字delete

点击跳转上一篇文章: 【C++11】:右值引用&移动语义&完美转发

前言

上篇文章我们学习了右值引用,那是C++11中新语法的重难点。本篇文章继续学习另外两个新语法:lambda表达式&function包装器。这两个语法在以后的学习和工作中也是经常使用的,所以要重点掌握,而它们的铺垫知识可变参数模板只需略作了解即可。

一,可变参数模板

1.1 简单认识

功能:可以接受可变参数的函数模板和类模板

下面就是一个基本可变参数的函数模板:

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数

1.2 STL容器中的empalce系列相关接口

在这里插入图片描述
在这里插入图片描述

首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对 push_back 和 emplace 系列接口的优势到底在哪里呢?

其实在一般情况下,这两种接口是等价的,但是在插入pair类型时,两者就会有区别

int main()
{
	bit::list<pair<bit::string, int>> lt1;
	
	// 构造pair + 拷贝/移动拷贝 pair 到 list 的节点中的data上
	pair<bit::string, int> kv("排序", 1);
	lt1.push_back(kv);
	
	// 直接构造pair参数包往下传,直接用pair参数包构造pair
	lt1.emplace_back("排序", 1);
}

emplace_back总体而言是更高效的

二,lambda表达式

在C++98中,如果自定义类型排序,需要用户定义排序时的比较规则:

struct Goods
{
	string _name;  // 名字
	double _price; // 价格
	int _evaluate; // 评价
	//.....

	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

struct ComparePriceLess
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price < g2._price;
	}
};

struct ComparePriceGreater
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	}
};

int main()
{
	 vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 
	3 }, { "菠萝", 1.5, 4 } };
	
	// 注意sort的第三个参数要用括号,因为sort是一个函数模板,参数传的是对象
	// 像优先级队列第三个参数就是要用类型,因为它是一个类模板
	 sort(v.begin(), v.end(), ComparePriceLess());
	 sort(v.begin(), v.end(), ComparePriceGreater());
}

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。

2.1 lambda表达式语法

(1) 语法格式

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0c570d33615546a1b99889f76fb4e976.png

(2) 基本使用

int main()
{
	auto add1 = [](int x, int y)->int {return x + y; };
	cout << add1(1, 2) << endl;
	
	// 函数体中有多条语句时
	auto func1 = []()->int
		{
			cout << "hello bit" << endl;
			cout << "hello world" << endl;
			return 0;
		};

	func1();
	cout << endl
}

(3) 一些细节问题

a. 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用

b. 参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略

c. 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略返回值类型明确情况下,也可省略,由编译器对返回类型进行推导

d. 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

代码示例

int main()
{
	// 无参时,参数列表可以省略,
	// 知道返回值类型时,返回值类型也可以省略,由编译器自动推导
	auto func2 = []
		{
			cout << "hello bit" << endl;
			cout << "hello world" << endl;
			return 0;
		};

	cout << func2() << endl;

	return 0;
}

现在来使用lambda表达式写商品排序问题:

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	
	//使用lambad
	//价格升序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
		{
			return g1._price < g2._price;
		});

	//价格降序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
		{
			return g1._price < g2._price;
		});

	return 0;
}

通过上述例子可以看出,lambda表达式实际上可以理解为匿名函数该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量

2.2 探索lambda底层

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象。

class Rate
{
public:
	Rate(double rate) 
	: _rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};

int main()
{
	// 函数对象
	double rate = 0.015;
	Rate r1(rate);

	cout << r1(10000, 2) << endl;

	// lambda
 	//捕捉列表对象的相当于以成员变量存在lambda类对象中的
 	//捕捉的本质是构造函数的初始化参数
	auto r2 = [rate](double monty, int year)->double
	{
		return monty * rate * year;
	};

	cout << r2(10000, 2) << endl;

	int x = 1, y = 2;
	auto r3 = [=](double monty, int year)->double
	{
		return monty * rate * year;
	};

	cout << r3(10000, 2) << endl;

	return 0;
}

从使用方式上来看,函数对象与lambda表达式完全一样。

函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可以直接将该变量捕获到

在这里插入图片描述

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()

三,包装器

3.1 function包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

使用时要包含头文件:

#include <functional>

类模板原型如下

template <class T> function;     
template <class Ret, class... Args>
class function<Ret(Args...)>;

//模板参数说明:
//Ret: 被调用函数的返回类型
//Args…:被调用函数的形参

包装器是用来包装可调用对象:函数指针对象,仿函数对象,lambed

使用方法如下

#include <functional>

int f(int a, int b)
{
	return a + b;
}

struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a + b;
	}
};

int main()
{
	// 包装可调用对象:函数指针对象,仿函数对象,lambed
	function<int(int, int)> f1 = f;
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int x, int y){return x + y; };

	cout << f1(1, 1) << endl;
	cout << f2(1, 1) << endl;
	cout << f3(1, 1) << endl;

	// 包装静态成员函数
	// 受类域限制,指定类域
	function<int(int, int)> f4 = Plus::plusi;
	cout << f4(1, 1) << endl;

	// 包装非静态成员函数
	// 方式1:
	// 1.非静态成员函数取地址要加&符号
	// 2.还需要传this指针
	function<double(Plus*,double, double)> f5 = &Plus::plusd;
	Plus pd;
	cout << f5(&pd, 1.1, 1.1) << endl;

	// 方式2:
	// 可以不传指针,直接传对象
	function<double(Plus, double, double)> f6 = &Plus::plusd;
	cout << f6(pd, 1.1, 1.1) << endl; // 有名对象
	cout << f6(Plus(), 1.1, 1.1) << endl; // 匿名对象

	return 0;
}

3.2 bind

bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,对可调用对象进行参数的调整(参数的顺序,参数的个数)本质返回的是一个仿函数对象

placeholders 是一个命名空间在使用bind进行参数调整时,需要展开

_1代表第一个实参
_2代表第二个实参
_3代表第三个实参
……

在这里插入图片描述

使用方法如下

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;

int Sub(int a, int b)
{
	return (a - b) * 10;
}

int SubX(int a, int b, int c)
{
	return (a - b - c) * 10;
}

int main()
{
	auto sub1 = bind(Sub, _1, _2);
	cout << sub1(10, 5) << endl;

	// bind 本身是一个函数模板,本质返回的是一个仿函数对象
	// 调整参数顺序(不常用)
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;

	// 调整参数个数(常用)
	auto sub3 = bind(Sub, 100, _1); // 绑定第1个参数
	cout << sub3(5) << endl;

	auto sub4 = bind(Sub, _1, 100); //  绑定第2个参数
	cout << sub4(5) << endl;

	// 分别绑死第123个参数
	auto sub5 = bind(SubX, 100, _1, _2);
	cout << sub5(5, 1) << endl;

	auto sub6 = bind(SubX, _1, 100, _2);
	cout << sub6(5, 1) << endl;

	auto sub7 = bind(SubX, _1, _2, 100);
	cout << sub7(5, 1) << endl;

	//bind 一般用于,绑死一些固定参数
	function<double(Plus, double, double)> f6 = &Plus::plusd;
	Plus pd;
	cout << f6(pd, 1.1, 1.1) << endl; // 有名对象
	cout << f6(Plus(), 1.1, 1.1) << endl; // 匿名对象

	function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f7(1.1, 1.1) << endl; // 有名对象

	return 0;
}

(1) 调整参数顺序示意图

时刻记住:_1代表第一个实参,_2代表第二个实参

在这里插入图片描述
(2) 调整参数个数的示意图

分别是绑定第1个参数,绑定第2个参数

在这里插入图片描述

四,类的新功能

4.1 默认成员函数

原来C++类中,有6个默认成员函数,C++11 新增了两个:移动构造函数和移动赋值运算符重载

对于新增的两个默认成员函数有一些需要注意的点如下:

  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
  • 默认移动赋值跟上面移动构造完全类似
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

代码示例如下

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}

private:
	bit::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = move(s1);

	Person s4;
	s4 = move(s2);

	return 0;
}

在这里插入图片描述

4.2 关键字default

功能:强制生成默认函数

4.3 关键字delete

功能:禁止生成默认函数

如果能想要限制某些默认函数的生成,在C++98中,是把该函数设置成private,并且只声明不实现,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}

	//防拷贝
	// C++98:只声明不实现,声明为私有
//private:
//	Person(const Person& p);
//	Person& operator=(const Person & p);

	//C++11
	//Person(const Person& p) = delete;
	//Person& operator=(const Person& p) = delete;

private:
	bit::string _name;
	int _age;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1980710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++数据保存到.csv文件中,并用opencv离线仿真显示

测试可能不是很方便&#xff0c;希望采集一次数据后期还可以使用&#xff0c;这里提供一个案例&#xff1a; 数据写入fosepose.csv //write.cpp #include <iostream> #include <fstream> #include <iomanip> #include <cstdint> #include <chrono…

达梦数据库的系统视图v$sql_plan

达梦数据库的系统视图v$sql_plan 达梦数据库的V$SQL_PLAN视图主要用于显示缓存中的SQL执行计划信息&#xff0c;在 ini 参数 USE_PLN_POOL !0 时才统计。通过查询这个视图&#xff0c;用户可以获取到缓存中的SQL语句的执行计划&#xff0c;这对于监控和分析数据库中的SQL执行情…

excel去掉小数点前的内容

可以使用"通配符"法 1、去除小数点后的内容 我们按CTRLH&#xff0c;调出查找替换功能 然后在查找内容里面&#xff0c;输入.* 星号是通配符&#xff0c;一点加通配符&#xff0c;表示将小数点后面的任意字符给去掉 2、去除小数点前的内容 我们按CTRLH&#xff0c;调…

Python处理和生成 Word 文档库之python-docx使用详解

概要 在日常工作中,处理和生成 Word 文档是一个常见的需求。Python 提供了一个强大的库 python-docx,使得操作 Word 文档变得简单和高效。python-docx 可以帮助我们创建、修改和读取 Word 文档,适用于各种应用场景,如自动化报告生成、批量文档处理等。本文将详细介绍 pyth…

Spring AOP:面向切面编程的最佳实践 ( 一 )

1.AOP思想 1.1.为什么需要面向切面编程 如果在一个类或者多个类的多个业务逻辑方法中, 在开始,结尾部分包含功能相同的代码称之为横切关注点也叫切面, 这种结构可能符合传统的面向对象编程&#xff08;OOP&#xff09;的封装特性, 但可能导致代码难以维护和扩展。 面向切面编…

Python 如何进行自然语言处理(NLTK, SpaCy)

自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是计算机科学和人工智能的一个重要领域&#xff0c;旨在实现计算机对人类语言的理解和处理。在Python中&#xff0c;有许多工具和库可以用于自然语言处理&#xff0c;其中最流行的两个是NLTK&#xff08;…

【Python脚本】定时任务脚本实现、自动关机等功能脚本实现(保姆篇)

文章目录 功能描述源码分析依赖参数配置数据校验多线程并发执行定时任务注册自动关机主程序 源码整合本篇小结 更多相关内容可查看 功能描述 需要python环境&#xff0c;详情可看主页python相关文章【Python】从0开始写脚本、Selenium详细教程、附源码案例&#xff08;保姆篇&…

3dsMax模型展开UV之后无法删除,3dsmax删除模型上已经展开的UV

3dsmax展开UV之后如何删除UV 方法二 如果不能重置UV通道&#xff0c;在实用工具》更多工具》UVW移除&#xff0c;选中模型&#xff0c;点击UVW移除&#xff0c;移除模型的UVW。

机器人抓取与操作的挑战与进展——挑战赛角度

从竞赛中看机器人抓取与操作的挑战与进展 前言一、国际机器人竞赛有哪些&#xff1f;二、感知方面的挑战与进展二、抓取方面的挑战与进展三、操作方面的挑战与进展总结 前言 本文根据最近的机器人抓取和操作挑战赛 (Robotic Grasping andManipulation Competitions (RGMCs))&a…

OCR图片矫正、表格检测及裁剪综合实践

问题描述 实际工程中&#xff0c;我们经常需要对图片进行预处理&#xff0c;比如&#xff1a; 1、图片是倾斜的 2、图片背景需要处理掉 3、图片的公章需要剔除 4、图片过暗&#xff0c;过亮 5、图片表格检测 6、图片表格版面分析 。。。。。。等等各种情况。 结果展示…

解决PuppeteerSharp生成PDF颜色问题的最佳实践

在现代网络开发中&#xff0c;使用爬虫技术生成PDF文件已成为一种常见需求。然而&#xff0c;开发者经常会遇到一些棘手的问题&#xff0c;其中之一便是使用PuppeteerSharp生成PDF时颜色丢失的问题。本篇文章将概述如何解决这一问题&#xff0c;并提供最佳实践和相关代码示例。…

TI音频功放TAS6511(二)

3.数字音频处理 芯片支持高级数字音频处理能力&#xff0c;包括&#xff1a; 高通滤波器/直流阻断 数字音量控制 PVDD的Foldback/AGL 热Foldback 双象限增益补偿 混合调制 实时负载诊断 低延迟路径 喇叭功率限制 1&#xff09;PVDD Foldback 本功能主要为了防止音频…

学习笔记--算法(双指针)2

复写零 链接&#xff1a;https://leetcode.cn/problems/duplicate-zeros/ 题目 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的…

分享一个基于微信小程序的旅游自助拼团系统(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

想业余时间做个网赚项目,有啥捷径?

前两年我陷入一段迷茫、浮躁、焦虑期。 主要原因是&#xff0c;心很大&#xff0c;力不足。 总想着找到一个高利润、高复购、少竞争的“蓝海”产品。 于是就面临一个尴尬的境地&#xff1a;普通业务看不上&#xff0c;蓝海业务找不着。 而且总想着做推广一步登天&#xff0…

程序员日志之DNF手游女鬼剑前瞻

目录 传送门正文日志1、概要女鬼剑 传送门 SpringMVC的源码解析&#xff08;精品&#xff09; Spring6的源码解析&#xff08;精品&#xff09; SpringBoot3框架&#xff08;精品&#xff09; MyBatis框架&#xff08;精品&#xff09; MyBatis-Plus SpringDataJPA SpringClou…

【教资 · 科目一】综合素质

【科目一】综合素质 单选题&#xff1a;29道/2分&#xff0c;共58分材料分析题&#xff1a;3道/14分&#xff0c;共42分写作题&#xff1a;1道&#xff0c;50分 卷面满分150分 —— 报告满分120分 及格分数99分以上 —— 及格分数70分 ‍ 考试内容&#xff1a; 职业理念&a…

Flink笔记整理(七)

Flink笔记整理&#xff08;七&#xff09; 文章目录 Flink笔记整理&#xff08;七&#xff09;九、容错机制9.1 检查点&#xff08;Checkpoint&#xff09;检查点的保存从检查点恢复状态检查点算法9.2 状态一致性9.3 端到端精确一次&#xff08;End-To-End Exactly-Once&#x…

中科亿海微SoM模组——光纤陀螺控制板

光纤陀螺控制板 光纤陀螺仪是一种高精度、高可靠性的惯性测量仪器&#xff0c;被广泛应用于导航、姿态控制等应用场景&#xff0c;具有非常重要的应用价值。 本文介绍的光纤陀螺控制板是基于中科亿海微自研的SiP芯片平台&#xff0c;以及光纤陀螺数字信号处理流程&#xff0c…

安泰高压功率放大器的作用以及应用有哪些

高压功率放大器是一种用于增强信号强度的电子设备。它的作用是将输入信号的功率放大到足够的水平&#xff0c;以便在系统中进行传输或执行特定的任务。高压功率放大器在各种领域都有广泛的应用&#xff0c;从通信到科学研究再到医疗设备等多个领域都可以找到其身影。 高压功率放…