当前位置: 首页 > news >正文

C++11 新特性 万能函数容器之std::function

std::function是 C++11 引入的一个非常强大的工具,位于<functional>头文件中。

简单来说,你可以把它理解为一个**“万能函数容器”“通用函数包装器”**。

在 C++11 之前,如果我们想存储一个函数指针,或者传递一个回调函数,往往受到类型的严格限制(比如函数指针无法直接存储 Lambda 表达式)。std::function通过**类型擦除(Type Erasure)**技术,统一了所有“可调用对象”的接口,让它们可以像普通变量一样被赋值、存储和传递。

🎯 核心概念:它能装什么?

只要函数签名(返回值和参数列表)匹配,std::function可以装下以下所有东西:

  1. 普通函数
  2. Lambda 表达式(尤其是带捕获的 Lambda,这是它最大的用途之一)
  3. 仿函数(函数对象):即重载了operator()的类实例
  4. 绑定后的成员函数(通过std::bind

💻 代码实战:统一江湖

看看下面这个例子,std::function如何让不同类型的函数“殊途同归”:

#include<iostream>#include<functional>// 必须包含的头文件#include<vector>usingnamespacestd;// 1. 普通函数intadd(inta,intb){returna+b;}// 2. 仿函数 (函数对象)structMultiplier{intoperator()(inta,intb){returna*b;}};intmain(){// 定义一个 std::function,规定它必须接收两个 int,返回一个 intstd::function<int(int,int)>func;// --- 场景 1: 存储普通函数 ---func=add;cout<<"普通函数结果: "<<func(3,4)<<endl;// 输出 7// --- 场景 2: 存储 Lambda 表达式 (带捕获) ---intfactor=10;func=[factor](inta,intb){return(a+b)*factor;};cout<<"Lambda 结果: "<<func(3,4)<<endl;// 输出 70// --- 场景 3: 存储仿函数 ---func=Multiplier();cout<<"仿函数结果: "<<func(3,4)<<endl;// 输出 12// --- 场景 4: 存入容器 (这是函数指针做不到的) ---vector<std::function<int(int,int)>>vec;vec.push_back(add);// 放入普通函数vec.push_back([](inta,intb){returna-b;});// 放入 Lambda// vec.push_back(Multiplier()); // 放入仿函数cout<<"容器调用: "<<vec[1](10,5)<<endl;// 输出 5return0;}

🆚 对比:std::functionvs 函数指针

为什么有了函数指针还需要它?看这张表就明白了:

特性函数指针 (int(*)(int, int))std::function<int(int, int)>
普通函数✅ 支持✅ 支持
Lambda (无捕获)✅ 支持 (可隐式转换)✅ 支持
Lambda (带捕获)不支持完美支持
仿函数/对象❌ 不支持✅ 支持
内存开销极小 (仅指针大小)稍大 (内部有多态实现的开销)
调用速度极快稍慢 (可能有间接跳转)

⚠️ 使用注意事项

  1. 空状态检查
    std::function可以像指针一样为空。如果你调用了一个空的std::function,程序会抛出std::bad_function_call异常。
    std::function<void()>func;// 默认为空if(func){// 使用前最好检查func();}
  2. 性能考量
    虽然std::function很方便,但它不是零开销的。在极度追求性能的循环中(比如每秒调用百万次),直接使用模板或者函数指针可能会更快。但在绝大多数业务逻辑、回调处理中,它的便利性远大于那一点点性能损耗。

🚀 C++17 新特性:类模板参数推导 (CTAD)

从 C++17 开始,你甚至不需要显式写出签名,编译器能自动推断:

automyFunc=[](intx,inty){returnx+y;};// C++17 之前: std::function<int(int, int)> f = myFunc;// C++17 及以后:std::function f=myFunc;// 编译器自动推导签名

总结:
std::function是现代 C++ 实现回调机制策略模式事件驱动的基石。它让你不再受限于函数指针的僵硬语法,可以更灵活地传递逻辑代码块。

统一了所有“可调用对象”的接口,让它们可以像普通变量一样被赋值、存储和传递。

⚙️ 它是如何做到的?

这正是通过你提到的**类型擦除(Type Erasure)**技术实现的。

std::function内部会创建一个抽象基类接口,并为每一种被存储的可调用对象(Lambda、仿函数等)生成一个具体的派生类。这个派生类会实现调用接口。当你调用func()时,std::function会通过内部的指针间接调用正确的对象,而对外则统一表现为void(int)的函数签名。

简单比喻:std::function就像一个万能插座,而各种函数、Lambda、仿函数就是不同规格的插头。类型擦除技术就是这个插座内部的“自适应”结构,它让所有插头都能插进去并使用。

http://www.jsqmd.com/news/657293/

相关文章:

  • 解锁Ryzen处理器的隐藏性能:SMUDebugTool完全指南
  • 3个技巧让百度网盘下载速度翻倍:直链解析工具实战指南
  • BI国产替代进入深水区:为什么企业真正重估的,不只是 Power BI 和 Tableau 的替代成本,而是数据分析能力的组织渗透率
  • 云端 LLM 思考,端侧 VLM 执行:探讨 侠客工坊Android 自动化测试与业务流转的终局
  • 【GitHub项目推荐--Octogent:给 Claude Code 装上“章鱼触手”的多智能体编排层】⭐
  • Redis实现分布式限流的几种方法
  • LLM-Graph-Builder:基于大语言模型的智能知识图谱构建解决方案
  • 博弈论算法精讲:从公平组合游戏到SG函数实战(ACM/OI选手必备)
  • 交直流混合微电网架构:拓扑优化与功率交互设计
  • 2026年3月SMT精密激光钢网供应商推荐分析,精密激光切割加工/SMT纳米阶梯钢网,SMT精密激光钢网源头厂家推荐分析 - 品牌推荐师
  • SITS2026智能生成能力雷达图(11维评估):从TypeScript泛型推导到Spring Boot事务链路补全,谁真正读懂了你的代码语义?
  • Adobe-GenP 3.0:解密Adobe全家桶通用补丁的技术实现与应用指南
  • 康耐视VisionPro:从“固定”到“灵活”,工业标定的实战进阶指南
  • 谷歌调整“水手计划”团队,浏览器智能体遇冷,新模型效率提升 50 倍!
  • 蓝桥杯单片机备赛避坑指南:从第九届省赛代码里学到的3个调试技巧与1个常见误区
  • MinerU 系列教程 第十一课:表格识别 - 有线与无线的双引擎
  • 如何为Windows安卓子系统打造完整的Android体验:MagiskOnWSALocal终极指南
  • EC开发tips
  • VRC Gesture Manager:Unity编辑器中实时预览VRChat虚拟形象动画的终极工具
  • 用Python和MATLAB搞定CCA:从数据预处理到结果可视化的完整实战指南
  • 用51单片机红外遥控器控制LED亮度(PWM调光保姆级教程)
  • SCL语言实战:在西门子PLC中构建高效FIFO栈数据结构
  • 3个强力技巧:用BilibiliDown实现B站音频高效提取完全指南
  • 【WindowsClear】一款面向 Windows 系统盘的 C盘清理工具,支持AppDate一键迁移到别的磁盘
  • 快速排序与希尔排序实战解析
  • 智能代码生成从“能用”到“飞快”的临界点:基于Transformer Decoder注意力机制重构的4种轻量化生成策略(含可复现PyTorch代码片段)
  • 手机号查QQ号终极指南:3步快速查询完整教程
  • Zotero文献格式化插件终极指南:一键告别杂乱文献库的完整解决方案
  • DeepMosaics终极指南:3个简单步骤掌握AI智能马赛克处理技术
  • MinerU 系列教程 第十二课:公式识别 - LaTeX 的自动生成