当前位置:

首页 > 编程开发 > c++ c++26 function_ref c++如何使用轻量级函数引用

c++ c++26 function_ref c++如何使用轻量级函数引用

absl::FunctionRef是轻量级、非拥有式的函数引用,大小固定16字节,避免std::function的堆分配与虚函数开销。适合高频回调等性能敏感场景,但不支持长期持有或跨作用域存储。C++26标准尚未纳入std::function_ref,需注意命名空间与生命周期管理。

好的,作为一位在C++领域摸爬滚打多年的老手,我来帮你把这篇关于轻量级函数引用的技术文章,重新整理成更像是出自一位资深工程师之手的专业分享。咱们直接开聊。 在处理高频回调或者性能敏感的函数时,你可能会遇到这样的场景:用`std::function`作为参数传入,结果性能分析器(profiler)告诉你,大量的内存分配(malloc)和虚函数调用成了瓶颈。其实,很多时候,一个更轻量级的方案就能解决问题——比如,今天我们要聊的`absl::FunctionRef`。

c++ c++26 function_ref c++如何使用轻量级函数引用

### absl::FunctionRef 是什么,和 std::function 有什么区别 简单来说,`absl::FunctionRef`是来自Abseil库的一个轻量级、非拥有式的函数引用。它不拷贝、不分配内存、也不管理生命周期,内部只保存一个指针(或者两个)加上一个调用器函数指针。在64位系统上,它的大小固定为16字节。相比之下,`std::function`就“重”多了,它是一个拥有所有权的封装,会深拷贝传入的可调用对象,甚至可能触发堆分配,特别是对于那些捕获了大量状态的lambda表达式,开销非常明显。 所以,一个很常见的性能陷阱就是:用`const std::function&`作为参数,传给一个会被高频调用的函数(比如遍历回调)。这时候,只需换上`absl::FunctionRef`,那些不必要的`malloc`和虚函数调用开销,就能轻松消除。 * **适用场景**:作为函数参数接收回调,尤其是性能敏感的路径,比如容器算法、事件分发、序列化钩子等。 * **不适用场景**:如果需要长期持有回调、跨作用域存储,或者需要复制给多个消费者,那就不适合用`FunctionRef`了。这时候应该考虑`absl::AnyInvocable`或者`std::function`。 * **兼容性**:要求C++17起步,Abseil v20230802或更新版本。GCC 9+、Clang 10+、MSVC 19.28+ 都能正常编译。 ### 如何正确声明和调用 absl::FunctionRef 声明语法是`absl::FunctionRef<返回类型(参数类型...)>`,注意括号里是完整的函数签名,而不是类型别名。调用方式跟普通函数一模一样,没有任何额外的语法负担。 这里有几个容易踩的坑。第一个,是把`absl::FunctionRef`写成了`absl::FunctionRef`,漏掉了括号。编译器会报一个很模糊的错误,实际上就是模板实参不匹配。第二个,是给`FunctionRef`的构造函数传了一个临时lambda的右值引用,但`FunctionRef`内部并不会延长这个lambda的生命周期,一旦lambda销毁,后续调用就是未定义行为。 * **正确写法**:`void Process(absl::FunctionRef op);` * **可安全传入**:`Process([](int a, int b) { return a + b; });`。因为lambda是纯右值,`FunctionRef`只存它的地址,在调用期间是安全的。 * **不可靠写法**:`auto f = [&cnt](int a) { cnt++; }; Process(f);`。如果`f`是局部变量,并且在`Process`返回后被销毁,那么后续对`f`的调用就会导致`use-after-free`。 * **关键建议**:如果需要捕获局部变量并长期使用,请改用`absl::AnyInvocable`,并确保其生命周期覆盖所有调用点。 ### C++26 标准版 function_ref 还没落地,别混淆 需要澄清一个普遍的误解:目前(2026年4月)C++26标准尚未发布,`std::function_ref`也还没有进入标准库。虽然委员会确实在讨论类似的提案(P0794R9),但截至C++26功能冻结时,这个特性并未被正式纳入。所以,你现在能看到的所有`function_ref`实现,都来自第三方库:Abseil的`absl::FunctionRef`、Trompeloeil的`trompeloeil::function_ref`,或者社区的头文件实现(如`tl::function_ref`)。 如果你看到编译错误提示“‘function_ref’ is not a member of ‘std’”,别怀疑自己配置错了,标准里确实还没它。强行用实验性标志(比如GCC的`-fconcepts`加自定义头文件)风险很高,不建议在生产环境中使用。 * **检查命名空间**:确认你写的是`absl::FunctionRef`,而不是`std::function_ref`。 * **确认Abseil链接**:确保头文件路径包含`absl/functional/function_ref.h`,并且正确链接了`absl::base`等依赖。 * **避免混用实现**:`tl::function_ref`和`absl::FunctionRef`接口相似,但ABI不兼容,不能相互转型。 ### 替代方案选型:什么时候该用 AnyInvocable 而不是 FunctionRef 核心判断依据只有一个:是否需要转移所有权。如果回调需要被存储、复制,或者跨线程传递,那`absl::FunctionRef`就不够用了——它不保证底层可调用对象的生存期。 举个例子,实现一个异步任务队列:用户注册一个回调后,主线程立即返回,后台线程稍后执行这个回调。这种情况下,必须用`absl::AnyInvocable`,因为它支持移动构造,能接管lambda捕获的栈变量或堆资源。而`FunctionRef`在这种场景下,极易引发`use-after-free`。 * `absl::FunctionRef`:适合“即用即弃”的短时回调,比如`std::for_each`的第三个参数。 * `absl::AnyInvocable`:适合需要move语义、生命周期管理,或者可能被多次调用的场合。 * **性能差异**:两者的构造成本接近,但`AnyInvocable`的析构可能触发释放逻辑,而`FunctionRef`的析构是空操作。 * **注意**:C++26的`std::move_only_function`(已定稿)行为更接近`AnyInvocable`,但它仍然不等于`FunctionRef`。 真正难处理的是跨作用域捕获与调用时机的错配。这种问题不会在编译时报错,而是会在运行时悄无声息地崩溃。所以,在决定使用`FunctionRef`之前,先问自己一句:这个lambda捕获的变量,在我调用它的时候,还活着吗?
本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发 C++
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

C++类构造与析构函数详解
C++类构造与析构函数详解

C++类构造与析构函数详解 C++这门语言,可以说是从C语言这棵大树上衍生出的高级果实,如今的应用普及度有目共睹。作为一种静态类型的通用编程语言,它厉害的地方在于融合了多种编程哲学——无论是传统的面向过程,还是主流的面向对象,乃至数据抽象、泛型编程这些高级概念,它都能很好地支持。正因为这份卓越的扩展

C++中std::upper
C++中std::upper

C++中std::upper_bound用法解析 在C++标准模板库(STL)的算法工具箱里,upper_bound() 绝对算得上是一把精准的“探针”。它的核心任务很明确:在一个已经排好序的区间 [first, last) 内,帮你快速定位到第一个**严格大于**指定值 value 的那个元素。这

C++常对象与成员解析
C++常对象与成员解析

C++中“常”概念全景解析:从对象、成员到指针与引用 在C++的世界里,“常量性”是一个强大的保障机制。它不仅仅是一个const关键字那么简单,而是构建健壮、安全程序的重要基石。今天,我们就来系统梳理一下围绕“常”的一系列概念:常成员、常对象、常指针与常引用。理解它们,是写出高质量C++代码的关键一

c++如何将结构体写入SQLite数据库_对象关系映射方案【进阶】
c++如何将结构体写入SQLite数据库_对象关系映射方案【进阶】

直接用SQLiteCAPI绑定结构体需要手动处理字段展平与顺序对齐,ORM库虽省代码但存在隐式行为风险。小项目建议原生绑定,中大型项目可搭配编译期校验使用轻量ORM。绑定std::string需传指针与长度,注意生命周期;浮点字段比较需设误差范围。

什么是编程,学习编程的意义是?
什么是编程,学习编程的意义是?

编程是编写程序指令,让设备按意图运行,常用Python、C等语言或Scratch图形化编程。学习编程能跟上科技时代,锻炼自学与解决问题能力,提升逻辑思维,代表行动力,推动事物前进。各司其职,编程者尽本分。

编程是门艺术_是编程艺术吗?
编程是门艺术_是编程艺术吗?

编程被争论是否为艺术。从定义看,编程是技能表达,但并非所有编程都是应用开发。写代码如音乐、文章,有独特风格。好代码需耐心与逻辑能力。编程是艺术,但应用开发不等同于编程艺术。

【编程的感受】
【编程的感受】

很多人提起编程,总觉得这是一门“不起眼”的学问——不就是写写代码嘛,能有多深?但真走进去了才会发现,编程里头的门道,远比表面看上去要丰富得多。 学习编程的过程中,最容易卡住的往往是那些基础语法。就拿那个常见的 for 循环来说吧,刚开始时怎么都绕不过弯来:为什么这么写能循环?循环条件到底怎么控制?一

对于懂得编程的人来说,编程对你来说有什么乐趣?编程大概是什么感觉?
对于懂得编程的人来说,编程对你来说有什么乐趣?编程大概是什么感觉?

解决编程问题后的痛快、完成作品的成就感、获取知识的满足感,以及用代码实现简化生活的惊喜与踏实,这些是编程独有的乐趣,只有真正写代码的人才能体会其中的酸甜苦辣、惊喜与满足,令人回味无穷。

趣味编程:少儿编程思维的启蒙之旅
趣味编程:少儿编程思维的启蒙之旅

Scratch图形化编程以拖拽积木块方式启蒙少儿编程思维,无需记忆代码语法。通过素材库、即时反馈和社区协作,帮助孩子在创作中理解顺序执行、条件判断等核心概念,锻炼逻辑、创造力和问题解决能力。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

PDF教程
PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
Postman macOS版
Postman macOS版
Mac

Postman是一套面向API全生命周期的开发与测试平台,可在Mac上发送和调试多种协议请求,管理集合、环境及认证信息,并完成脚本测试、模拟服务、监控、文档发布和团队协作。它适合开发、测试及运维人员使用,提供个人免费方案。

Docker Desktop macOS版
Docker Desktop macOS版
Mac

Docker Desktop 是 Docker 官方为 Mac 提供的容器开发环境,集成 Docker Engine、命令行工具、Compose、Build、Kubernetes 与图形化管理界面。

Android Studio macOS版
Android Studio macOS版
Mac

Android Studio 是 Google 面向安卓应用开发提供的官方集成开发环境,基于 IntelliJ IDEA 构建。它整合智能代码编辑、Gradle 构建、界面预览、设备模拟、性能分析、调试测试及 APK 分析等工具。

WINDOWS 更多
Postman Windows版
Postman Windows版
Windows

Postman 是面向 API 全生命周期的开发与协作平台。Windows 桌面版可用于构建和发送 HTTP、GraphQL、gRPC、WebSocket、MQTT 等请求,并提供环境变量、集合、脚本测试、模拟服务、监控、文档及团队工作区等

Docker Desktop Windows版
Docker Desktop Windows版
Windows

Docker Desktop 为 Windows 开发者提供集成式容器环境,内置 Docker Engine、命令行工具、Compose、Build 与 Kubernetes 等组件,可在本机完成镜像构建、容器运行、日志查看和资源管理。

Android Studio Windows版
Android Studio Windows版
Windows

Android Studio 是 Google 面向 Android 应用开发提供的官方集成开发环境,以 IntelliJ IDEA 为基础,集成智能代码编辑、Gradle 构建、设备模拟、界面预览、调试测试及性能分析工具,