C++11新特性(下)----《Hello C++ Wrold!》(26)--(C/C++)

C++11新特性(下)----《Hello C++ Wrold!》(26)--(C/C++)

文章目录

前言

在 C++11 标准带来的诸多革命性特性中,“简化代码编写” 与 “统一可调用对象管理” 是两大核心目标。lambda 表达式解决了传统仿函数 “定义繁琐、复用性低” 的痛点,让局部场景下的自定义逻辑(如排序规则、回调函数)能以更简洁的匿名函数形式实现;可变参数模板则打破了模板参数数量固定的限制,为 STL 容器(如emplace_back)和通用函数设计提供了灵活的参数处理能力;而 function 包装器与 bind 函数,则进一步整合了函数指针、仿函数、lambda 等不同类型的可调用对象,实现了统一管理与参数适配,甚至让可调用对象存储到容器中成为可能。

这些特性并非孤立存在 ——lambda 的底层依赖仿函数实现,可变参数模板为emplace系列接口提供了技术支撑,function 与 bind 则基于前两者的特性,解决了 “不同可调用对象类型不统一” 的问题。本文将从实际开发需求出发,先讲解 lambda 表达式的语法规则与捕获逻辑,再深入可变参数模板的展开方法与应用场景,最后通过 function 包装器与 bind 函数的实例,展示如何统一管理各类可调用对象、灵活调整参数顺序与固定参数值。

文中不仅会拆解易混淆的概念(如 lambda 的 mutable 修饰符作用、placeholders 占位符的使用规则),还会结合具体代码示例(如用 vector 存储 function 对象、用 bind 适配类成员函数),帮助读者理解这些特性的设计逻辑与实际价值。同时,文末的作业解析也将围绕 C++11 常见考点(如范围 for 的适用场景)展开,进一步巩固对标准特性的理解,为后续高效使用 C++11 进行开发打下基础。

lambda表达式

比如:一个类里面有很多成员变量,想对他们都进行排序,但是每次比较的逻辑不一样,而且还要每次都写一个类去比较,太麻烦了,所以就引入了lambda表达式

lambda表达式是局部的匿名的函数对象–所以sort填仿函数那里可以写这个

底层其实是用的仿函数实现的
这个表达式不存在重载这个说法哈

引申:auto f1 = (){}; 和auto f2 = (){}; f1和f2也不是同一类型 f1=f2的话会报错

也就是. lambda表达式之间不能相互赋值,即使看起来类型相同
这个表达式的格式:[捕捉列表](参数列表)(mutable)-> 返回值类型 { 函数体 }; 捕获列表的话可以捕获这个表达式所在域的局部变量--函数也可以捕捉哈 --全局域里的不用捕获也能用 --这种的强行捕获可能会报错 参数列表里面不传参的话,()也可以省了 这个mutable的话可以取消捕获列表里面东西默认的const性 但是在用该修饰符的时候,参数列表不能省略 --如果捕获的东西本来是const的话,加了mutable也变不了 注意:捕获列表默认是const的 ->返回值类型:这个一般可以不用写,编译器会推导 
关于捕获列表:(没用mutable的话,捕获的东西都不能被修改)

[var]:表示值传递方式捕捉变量var–var的类型会跟传递过来的一模一样

[=]:表示值传递方式捕获所有父作用域中的变量(包括this)

[&var]:表示引用传递捕捉变量var eg:[&x,&y]--这个跟取地址不要混了

[&]
:表示引用传递捕捉所有父作用域中的变量(包括this)

这里面的&=:如果是[&,x]那就只有x是值传递,其他都是引用

注意:捕捉列表不允许变量重复传递,否则就会导致编译错误[=,a]就不行,a捕获了两次
用法展示:
引申:可调用对象的四个存储方法:函数指针,仿函数,lambda表达式,用包装器搞到容器里面

可变参数模板

就是让模板参数可以是不确定的数目

其实日常自己写的话很少用的
template<class... Args>voidShowList(Args... args){} Args表示一个包含零个或多个类型的参数包 args也是参数包,里面可以是0或多个参数 ...是展开参数包的操作(放在参数包后面--除了模板参数声明那里) 但是这个参数包想知道里面的参数是啥的话比较困难--不支持eg:arg[1]这样 

展开参数包的方法

1.递归展开参数包

2.逗号表达式展开参数包

应用

假设有个日期类Date template<class... Args> Date*Create(Args... args){ Date* ret =newDate(args...);return ret;} 可以Date* p1 =Create(2025,9,7);这样来用 或者 Date d(2025,9,7); Date* p1 =Create(d);也行 
emplace_back有可变参数模板 push_back没用 emplace_back可以直接传零散的参数进去 但是push_back必须要先把零散的参数搞成临时对象再传进去 

包装器

fiction包装器

function包装器也叫作适配器 他需要头文件#include <functional>

作用:比如:

并且包装器可以让可调用对象存储到容器中去了
用法: function的类模板原型:template<classT> function;//这是模板的声明template<classRet,class... Args>classfunction<Ret(Args...)>; Ret: 被调用函数的返回类型 Args…:被调用函数的形参 使用:doublef(double i){return i /2;}structFunctor{doubleoperator()(double d){return d /3;}}; vector<function<double(double)>> v ={ f,[](double d)->double{return d /4;},Functor()}; 第一个double是返回值类型 第二个double是形参类型--两个double类型的形参的话就写(double,double)
用法还有eg: map<string,function<int(int)>> map1 ={{"a",[](int x){return x;}},{"y",[](int x){return x+1;}}}; 用的话就:int x =0;int a = map1["a"](x);//注意理解map1["a"]取出包装器.....

bind函数

这个的头文件也是#include <functional>

bind
是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表

这个的作用也就是把参数的顺序改了+可以固定一些参数的值
这里固定参数的值跟直接给缺省值的比较:

缺省值只能给固定的参数一个固定值

这里就不一样了
用法: 对于普通的函数:doublePlus(int a,int b,double rate){return(a + b)* rate;} function<double(int,int)> Plus1 =bind(Plus, placeholders::_2, placeholders::_1,4.0); 这个的话就是rate固定是4.0 然后Plus1(9,8) 这里的placeholders::_1表示的是(9,8)里面的9然后会传给b这样 如果想固定的值在中间的话 eg:doublePlus(int a,double rate,int b){return(a + b)* rate;} function<double(int,int)> Plus1 =bind(Plus, placeholders::_2,4.0,placeholders::_1);Plus1(9,8);--8最终给了a 这个依旧是_1,_2这样的哈 
用法: 对于类里面的成员函数的话: 如果函数是静态的,那到没啥区别(但是记得加上类域) 如果函数不是静态的: 函数后面要跟上这个对象或者对象的指针才行 --函数是静态的话,可以加那个也可以不加 eg:classSubType{public:intsub(int a,int b){return a - b;}}; SubType st; function<double(int,int)> Sub2 =bind(&SubType::sub,&st, placeholders::_1, placeholders::_2,3); function<double(int,int)> Sub2 =bind(&SubType::sub, st, placeholders::_1, placeholders::_2,3); function<double(int,int)> Sub3 =bind(&SubType::sub,SubType(), placeholders::_1, placeholders::_2,3);//这里用的是匿名对象 引申:类里面的函数在外面用的时候必须要到类域里面去找才行 

作业部分

在这里插入图片描述
下面关于范围for说法错误的是(C) A.范围for可以直接应用在数组上 B.对于STL提供的所有容器,均可以使用范围for依次访问器元素 C.使用范围for操作stack,可以简化代码 //上面说的是容器,但是stack是容器适配器 D.对于自定义类型,想要支持范围for,必须提供begin和end迭代器 E.范围for编译器最终是将其转化为迭代器来进行处理的 
在这里插入图片描述

Read more

我的算法修炼之路--5——专破“思维陷阱”,那些让你拍案叫绝的非常规秒解

我的算法修炼之路--5——专破“思维陷阱”,那些让你拍案叫绝的非常规秒解

💗博主介绍:计算机专业的一枚大学生 来自重庆 @燃于AC之乐✌专注于C++技术栈,算法,竞赛领域,技术学习和项目实战✌💗 💗根据博主的学习进度更新(可能不及时) 💗后续更新主要内容:C语言,数据结构,C++、linux(系统编程和网络编程)、MySQL、Redis、QT、Python、Git、爬虫、数据可视化、小程序、AI大模型接入,C++实战项目与学习分享。 👇🏻 精彩专栏 推荐订阅👇🏻 点击进入🌌作者专栏🌌: 算法画解 ✅ C++ ✅ 🌟算法相关题目点击即可进入实操🌟 感兴趣的可以先收藏起来,请多多支持,还有大家有相关问题都可以给我留言咨询,希望希望共同交流心得,一起进步,你我陪伴,学习路上不孤单! 文章目录 * 前言 * 题目清单 * 1.Metoer Shower(流星雨) * 2.

By Ne0inhk
栈与队列:数据结构中的 孪生兄弟 的本质差异

栈与队列:数据结构中的 孪生兄弟 的本质差异

个人主页-爱因斯晨 文章专栏-数据结构 文章目录 * 个人主页-爱因斯晨 * 文章专栏-数据结构 * 1. 引言 * 2. 栈和队列的概念界定 * 2.1 栈 * 2.2 队列 * 3. 栈和队列的核心差异 * 4. 栈的 C 语言实现 * 4.1 栈的结构体定义 * 4.2 栈的基本操作函数 * 4.2.1 初始化栈 * 4.2.2 判断栈是否为空 * 4.2.3 判断栈是否已满 * 4.2.4 入栈操作 * 4.2.5 出栈操作 * 5. 队列的 C

By Ne0inhk
【算法精讲】深度优先搜索(DFS),一文带你彻底掌握!✨

【算法精讲】深度优先搜索(DFS),一文带你彻底掌握!✨

前言 亲爱的同学们,大家好!👋 今天我要和大家分享一个在算法世界中非常重要的搜索策略——深度优先搜索(Depth-First Search, DFS)。作为一名编程老师,我经常看到很多同学在面对图论和树的遍历问题时感到困惑,特别是当需要选择合适的搜索策略时。🤔 深度优先搜索就像是探险家在迷宫中的探索,总是沿着一条路径走到尽头,才会返回尝试其他路径。这种"一条路走到黑"的策略在解决许多实际问题中非常有效!今天,我们将一起深入了解DFS的原理、实现和应用,让你在算法的世界里多一把利器!💪 准备好开启这段探索之旅了吗?让我们一起出发吧!🚀 知识点说明 1. DFS的基本概念 深度优先搜索(DFS)是一种用于遍历或搜索树或图的算法。它的核心思想是:从起始节点开始,尽可能深入地探索一条路径,直到无法继续为止,然后回溯到前一个节点,继续探索其他未访问的路径。📝 DFS的三个关键特点: * 优先深度:算法会尽可能深入地探索一条路径,而不是广泛地探索所有可能的路径 * 使用栈:DFS通常使用栈(或递归,隐式使用系统栈)来记住探索过程中的节点 * 回溯:当无法继续深入时

By Ne0inhk