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

目录

1、什么是lambda表达式

2、lambda表达式

2.1 lambda表达式语法

2.2 捕获列表说明

3、function包装器

 bind


1、什么是lambda表达式

Lambda表达式是一种在C++11中引入的功能,允许我们定义匿名函数。它的语法比较简洁,可以方便地在需要函数对象的地方使用,例如STL算法、线程、智能指针等。

在C++98中,对一个数据集合中的元素进行排序:

// 默认按照小于比较,排出来结果是升序
std::sort ( array , array + sizeof ( array ) / sizeof ( array [ 0 ]));
// 如果需要降序,需要改变元素的比较规则
std::sort ( array , array + sizeof ( array ) / sizeof ( array [ 0 ]), greater < int > ());

但是如果是自定义类型的话,我们就需要实现仿函数 

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
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& gl, const Goods& gr){return gl._price < gr._price;}
};
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}

这样虽然可以比较,但是每次都要实现一个类,那么就有点不方便了,所以C++11中提出了lambda表达式。

2、lambda表达式

2.1 lambda表达式语法

[capture-list] (parameters) mutable -> return-type { statement }

其中各部分的含义如下:

capture-list:捕获列表,用于捕获局部变量以供lambda函数使用。可以是空的[],表示不捕获任何变量;也可以是[&]表示按引用捕获所有变量;还可以是[=]表示按值捕获所有变量;还可以是[a, &b]表示按值捕获变量a,按引用捕获变量b。

  • [ ]:空的捕捉列表,表示不捕获任何外部变量。
  • [&]:按引用捕获所有外部变量,可以在Lambda表达式中修改这些变量。
  • [=]:按值捕获所有外部变量,以副本的方式在Lambda表达式中使用这些变量,不允许修改这些变量。
  • [var1, var2, ...]:按值捕获指定的外部变量var1、var2等。

parameters:参数列表,与普通函数的参数列表相同。

mutable:可选关键字,用于指示lambda函数是否可以修改其捕获的变量。如果使用了mutable关键字,则lambda函数可以修改以值方式捕获的变量的副本。

mutable关键字用于在Lambda表达式中声明捕获的变量为可变的,即使这些变量是以值方式捕获的,也可以在Lambda函数体中修改它们的值。

return-type:返回类型,与普通函数的返回类型相同。可以省略,如果省略了,编译器会根据statement中的返回语句推导返回类型。

statement:函数体,与普通函数的函数体相同。

Lambda表达式的书写格式允许我们在需要时捕获局部变量,并定义一个匿名函数对象,可以在需要时直接调用。

代码示例:

int main()
{// 最简单的lambda表达式, 该lambda表达式没有任何意义[] {};// 省略参数列表和返回值类型,返回值类型由编译器推导为intint a = 3, b = 4;[=] {return a + 3; };// 省略了返回值类型,无返回值类型auto fun1 = [&](int c) {b = a + c; };fun1(10);cout<< a << " " << b << endl;// 各部分都很完善的lambda函数auto fun2 = [=, &b](int c)->int {return b += a + c; };cout << fun2(10) << endl;// 复制捕捉xint x = 10;auto add_x = [x](int a) mutable { x *= 2; return a + x; };cout << add_x(10) << endl;return 0;
}

这样一来上面自定义类型的比较就方便很多:

int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), [](Goods& a, Goods& b) {return a._price < b._price; });sort(v.begin(), v.end(), [](Goods& a, Goods& b) {return a._price > b._price; });return 0;
}

2.2 捕获列表说明

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用

[var] :表示值传递方式捕捉变量 var
[=] :表示值传递方式捕获所有父作用域中的变量 ( 包括 this)
[&var] :表示引用传递捕捉变量 var
[&] :表示引用传递捕捉所有父作用域中的变量 ( 包括 this)
[this] :表示值传递方式捕捉当前的 this 指针

需要注意的是: 

父作用域指包含 lambda 函数的语句块
语法上捕捉列表可由多个捕捉项组成,并以逗号分割
比如: [=, &a, &b] :以引用传递的方式捕捉变量 a b ,值传递方式捕捉其他所有变量
[& a, this] :值传递方式捕捉变量 a this ,引用方式捕捉其他变量
捕捉列表不允许变量重复传递,否则就会导致编译错误
比如: [=, a] = 已经以值传递方式捕捉了所有变量,捕捉 a 重复
lambda表达式之间不能相互赋值,即使看起来类型相同
但是可以使用一个lambda表达式拷贝构造一个新的副本
void (*PF)();
int main()
{auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };// 此处先不解释原因,等lambda表达式底层实现原理看完后,大家就清楚了//f1 = f2;   // 编译失败--->提示找不到operator=()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF = f2;PF();return 0;
}
实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()

3、function包装器

std::function 是 C++11 中引入的一个模板类,它是一个可复制的、可存储任意可调用对象(函数、Lambda表达式、函数对象等)的对象。它提供了一种通用的方式来封装函数,使得函数可以像对象一样进行传递、存储和使用。使用 std::function 可以轻松地创建一个函数对象,并将其作为参数传递给其他函数,或者将其存储在容器中等等。

可变模板参数:

可变参数模板是 C++11 引入的一个功能,允许函数接受任意数量和类型的参数。

#include <iostream>// 递归终止函数
void print() {std::cout << std::endl;
}template<typename T, typename... Args>
void print(const T& first, const Args&... args) {std::cout << first << " ";print(args...);
}int main() {print(1, 2.5, "Hello", 'a');return 0;
}

 一个接受任意数量的参数(通过模板和参数包实现),另一个是基本情况,即不接受任何参数,用于终止递归。当调用 print 函数时,参数被逐个打印出来,直到没有剩余参数。

// 类模板原型如下
template < class T > function ;     // undefined
template < class Ret , class ... Args >
class function < Ret ( Args ...) > ;
模板参数说明:
Ret : 被调用函数的返回类型
Args… :被调用函数的形参
#include <iostream>
#include <functional>// 函数模板,接受一个std::function类型的参数
void print_message(std::function<void()> func) {func(); // 调用传入的函数对象
}int main() {// Lambda表达式作为参数传递给print_message函数print_message([]() {std::cout << "Hello, std::function!" << std::endl;});// 函数对象作为参数传递给print_message函数struct Printer {void operator()() const {std::cout << "Printing from a function object!" << std::endl;}};print_message(Printer());return 0;
}

print_message 函数接受一个 std::function<void()> 类型的参数,表示接受无参数并且返回值为 void 的可调用对象。通过将 Lambda 表达式和函数对象传递给 print_message 函数,我们可以轻松地调用它们。 

得到输出

Hello, std::function!
Printing from a function object!

 bind

std::bind 是 C++11 中引入的函数模板,用于创建一个可调用对象,并将其与指定的函数或函数对象绑定在一起。它可以用来创建一个函数对象,该函数对象可以延迟调用目标函数,并在需要时传递额外的参数或更改参数顺序。

#include <iostream>
#include <functional>// 目标函数
void greet(const std::string& name, int age) {std::cout << "Hello, " << name << "! You are " << age << " years old." << std::endl;
}int main() {// 使用 std::bind 绑定目标函数和部分参数auto greetSomeone = std::bind(greet, "Alice", std::placeholders::_2);  //指定参数位置// 调用绑定后的函数对象,并传递剩余的参数greetSomeone(0,30);  //这里的0没有意义,只是为了表示第二个参数对应的位置return 0;
}

greet 函数接受两个参数:一个 std::string 类型的名称和一个 int 类型的年龄。使用 std::bind,我们将 greet 函数与参数 "Alice" 绑定在一起,并指定 std::placeholders::_2 作为占位符,表示第二个参数的位置。然后,我们创建了一个名为 greetSomeone 的函数对象,它只需要一个参数,即年龄。当我们调用 greetSomeone 时,它会以 "Alice" 作为名称,并传递给定的年龄参数。

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

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

相关文章

Idea 自动生成测试

先添加测试依赖&#xff01;&#xff01; <!--Junit单元测试依赖--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.9.1</version><scope>test</scope><…

2024年手把手教你安装iMazing3 中文版图文激活教程

热门苹果设备备份管理软件iMazing日前正式发布了全新的 3.0 版本。它采用了全新的设计和架构&#xff0c;并且率先支持Apple Vision Pro安装软件和导入数据。团队表示&#xff0c;这是市场首个第三方支持Apple Vision Pro的管理工具。 iMazing是一款兼容Win和Mac的iOS设备管理…

什么是sql注入攻击?

什么是sql注入攻击? 就是在对用户的输入的数据没有进行很好的处理,从而导致输入变成了sql语句的一部分了,正常来说用户输入的数据只是参数,但是不是的 举个例子吧 比如在登录的时候,用户名是fjg,密码是123456,但是黑客了,不知道密码,但是知道你sql的相关处理的不好,黑客这样操…

杭电 OJ 1000-1009 Java解法

Problem Set 1000 高精度 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scnew Scanner(System.in);while(sc.hasNext()){System.out.println(sc.nextBigInteger().add(sc.nextBigInteger()));}} } 1001 位运算防爆 1000…

解决Linux中磁盘满/dev/vda1使用率100%问题

发现根目录下占用100%&#xff0c;具体还要排场到底是有哪些大文件占用 那么就在根目录下查询各个子文件夹的占用状态&#xff0c;有过大不用的即可删除 df -h *我的磁盘是100G&#xff0c;但这些总共加起来也接近不了这个数值 那就是有可能出现 已删除空间却没有释放的进程…

嵌入式学习day09

每日面试题 堆栈溢出的原因有哪些 ①函数调用层次太深&#xff0c;函数递归调用时&#xff0c;系统要在栈中不断保存函数调用时的线程和产生的变量&#xff0c;递归调用太深&#xff0c;会造成栈溢出&#xff0c;这时递归无法返还。 ②动态申请空间使用后没有释放。由于C语言…

(centos)yum安装mysql8.4

1.MySQL官方已经提供了适用于不同Linux发行版的预构建软件包&#xff0c;包括适用于CentOS的Yum Repository MySQL :: MySQL Community Downloads 2.在/usr/local文件夹下创建mysql文件夹&#xff0c;将下载的rpm文件放到目录下 3.执行安装命令 yum install mysql-community-…

Vue3 + Vite + TypeScript + Element-Plus创建管理系统项目

官方文档 Vue3官网 Vite官方中文文档 创建项目 使用npm命令创建项目&#xff1a; npm create vitelatest输入项目名称&#xff1a; ? Project name:项目名选择vue&#xff1a; ? Select a framework: - Use arrow-keys. Return to submit.Vanilla > VueReactPrea…

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘ 原因是notebook新版本没有notebook.auth 直接输入以下命令即可设置密码 jupyter notebook password

「JavaEE」线程安全2:内存可见性问题 wait、notify

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;JavaEE &#x1f387;欢迎点赞收藏加关注哦&#xff01; 内存可见性问题& wait、notify &#x1f349;Java 标准库的线程安全类&#x1f349;内存可见性问题&#x1f34c;volatile 关键字 …

M2M vs. IoT?

有任何关于GSMA\IOT\eSIM\RSP\业务应用场景相关的问题&#xff0c;欢迎W: xiangcunge59 一起讨论, 共同进步 (加的时候请注明: 来自CSDN-iot). 连接设备已经开辟了创造价值和解决重大世界问题的广泛机会&#xff0c;例如可持续发展。 今天&#xff0c;我们网络设备的方式可…

【linuxC语言】vfork、wait与waitpid函数

文章目录 前言一、函数使用1.1 vfork1.2 wait1.3 waitpid 二、示例代码总结 前言 在Linux系统编程中&#xff0c;vfork()、wait() 和 waitpid() 函数是处理进程管理和控制流的重要工具。这些函数允许我们创建新进程、等待子进程结束并获取其退出状态&#xff0c;从而实现进程间…

GDPU JavaWeb 猜字母游戏

他在对你重定向打卡的大饼与立即跳转到你面前的谎言之间反复横跳。 sendRedirect与forward sendRedirect与forward区别 sendRedirect用于将请求重定向到另一个资源&#xff0c;可以是同一个应用程序内的其他 Servlet&#xff0c;也可以是其他 Web 应用程序的资源&#xff0c;…

配电网光伏储能双层优化配置模型(选址定容)

随着主动配电网中不可控分布式电源(distributed generation,DG)渗透率的不断提高,其给配电网规划运行提 出了更大的挑战,可能导致配电网电压越限、清洁能源弃电 等问题。基于此,该文在规划阶段充分应用不同类型 DG 和 负荷的时序性和潮流计算特性,以及引入不同恢复率的激励…

telement-plus中table合并行或者列

#element-plus中会有自带的合并方法 通过给 table 传入span-method方法可以实现合并行或列&#xff0c; 方法的参数是一个对象&#xff0c;里面包含当前行 row、当前列 column、当前行号 rowIndex、当前列号 columnIndex 四个属性。 该函数可以返回一个包含两个元素的数组&…

农作物害虫检测数据集VOC+YOLO格式18975张97类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;18975 标注数量(xml文件个数)&#xff1a;18975 标注数量(txt文件个数)&#xff1a;18975 标…

minio忘记密码

安装minio 对象存储minio-CSDN博客 cat << EOF > /etc/default/minio# Volume to be used for Minio server. MINIO_VOLUMES"/data"# Use if you want to run Minio on a custom port. #MINIO_OPTS"--address :9001" MINIO_OPTS"--address …

卷积神经网络要点和难点实际案例和代码解析

卷积神经网络(Convolutional Neural Networks,CNN)是一类包含卷积计算且具有深度结构的前馈神经网络,是深度学习的代表算法之一。卷积神经网络仿造生物的视知觉机制构建,可以进行监督学习和非监督学习,其隐含层内的卷积核参数共享和层间连接的稀疏性使得卷积神经网络能够…

Boost读写xml

Boost读写xml 文章目录 Boost读写xml写在前面准备工作简单读写写xml读xml键值查找遍历 设置默认值异常处理 具有属性的xml写xml读xml键值查找遍历 知识补充 写在前面 ​ 前面我们讲过了如何使用Boost读写ini文件&#xff0c;这一篇我们将介绍如何用Boost读写xml文件。 ​ XML…

C++入门系列-类对象模型this指针

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 类对象模型 如何计算类对象的大小 class A { public:void printA(){cout << _a << endl;} private:char _a; }; 算算看&#xff0c;这个类的大小是多少 我们知道…