C++ STL之std::function与可调用对象包装详解

【C++】第二十七节—C++11(下) | 可变参数模版+新的类功能+STL中一些变化+包装器 本文介绍了C++11中可变参数模板的基本概念和使用方法,包括参数包的声明、扩展方式以及在实际编程中的应用。文章详细讲解了可变参数模板的原理,通过编译时实例化处理不同数量和类型的参数。同时介绍了std::function包装器和bind适配器的使用,它们可以统一处理函数指针、仿函数、lambda等可调用对象。此外还探讨了C++11新增的移动语义、emplace系列接口等特性,并通过具体代码示例展示了这些新特性的实际应用场景,帮助开发者编写更高效灵活的泛型代码。 阅读详情

C++ STL之std::function与可调用对象包装详解

一、用法速查

#include <functional>
#include <iostream>

int FreeFunc(int a) { return a * 2; }

struct FuncObj {
    int operator()(int a) { return a + 1; }
};

int main() {
    // 包装自由函数
    std::function<int(int)> f1 = FreeFunc;

    // 包装函数对象
    std::function<int(int)> f2 = FuncObj{};

    // 包装 Lambda
    std::function<int(int)> f3 = [](int a) { return a - 1; };

    // 包装 std::bind 结果
    auto add = [](int x, int y) { return x + y; };
    std::function<int(int)> f4 = std::bind(add, 10, std::placeholders::_1);

    // std::mem_fn — 包装成员函数
    struct S { int Get() const { return 42; } };
    auto fn = std::mem_fn(&S::Get);
    S s;
    std::cout << fn(s) << "\n";  // 42

    // C++17 std::not_fn — 取反
    std::function<bool(int)> is_even = [](int x) { return x % 2 == 0; };
    auto is_odd = std::not_fn(is_even);
    std::cout << is_odd(3) << "\n";  // true

    // C++20 std::bind_front — 绑定前 N 个参数
    auto mul = [](int a, int b, int c) { return a * b * c; };
    auto mul_2_3 = std::bind_front(mul, 2, 3);
    std::cout << mul_2_3(4) << "\n";  // 24
}

二、底层原理

2.1 类型擦除

std::function 的核心机制是类型擦除。无论包装的是 Lambda、函数指针还是函数对象,对外都呈现统一的 std::function<Signature> 接口。实现上依赖桥接模式:

template <typename T>
struct FunctionImpl;  // 特化版本,存储实际可调用对象

// 伪码示意:基类虚函数桥接
struct CallableBase { virtual ~CallableBase() = default; };

template <typename F, typename R, typename... Args>
struct CallableImpl : CallableBase {
    F f;
    R Invoke(Args... args) { return f(args...); }
};

是

否

std::function 对象

类型擦除桥接层

可调用对象大小
≤ SBO 阈值?

栈上存储 (SBO)

堆上分配

直接调用小对象

通过虚函数表间接调用

2.2 小对象优化 vs 堆分配

  • SBO(Small Buffer Optimization):标准库在 std::function 内部预留固定大小缓冲区(通常是 sizeof(void*) * 3 左右)。当可调用对象大小不超过缓冲区时,直接就地构造,避免堆分配。
  • 堆分配:当对象超出 SBO 容量(如捕获大量变量的 Lambda 或大型函数对象),std::function 退而使用 operator new 分配堆内存。

MSVC STL 的实现中,当可调用对象大小 ≤ 32 字节且对齐 ≤ alignof(max_align_t) 时走 SBO。GCC libstdc++ 类似,阈值约 24 字节。

2.3 std::bind 占位符与求值延迟

std::bind 返回一个函数对象,内部保存绑定的参数和占位符。占位符 _1, _2, ... 是命名空间 std::placeholders 中的特殊对象,调用时替换为实际传入的参数。

auto f = std::bind(add, 10, _1);  // 不立即求值
f(5);  // 此时才求值,等价于 add(10, 5)

延迟求值的好处是可以把部分参数固定后传递回调,但 C++17 后更推荐 Lambda 替代 bind,因为 Lambda 更直观且不会被 forward 语义坑。

2.4 std::mem_fn

将成员函数指针包装为可调用对象,支持引用、指针和智能指针调用:

auto get = std::mem_fn(&S::Get);
get(s);       // s.Get()
get(&s);      // s.Get()
get(sp);      // sp->Get()

2.5 std::not_fn (C++17)

返回一个取反后的函数对象,等价于 [&](auto&&... args) { return !f(args...); }。优于 not1/not2,支持任意元数。

2.6 std::bind_front (C++20)

固定前 N 个参数,返回剩余参数由调用处提供的函数对象:

auto f = std::bind_front(func, a, b);
f(c, d);  // 等价于 func(a, b, c, d)

与 std::bind 的区别:不处理占位符,语义更简单,性能更好。

三、Lambda vs std::function

Lambda 不是 std::function。Lambda 是编译器生成的匿名闭包类型,有自己的 operator()。std::function 是类型擦除的包装器。

std::function

类型擦除运行时多态

可赋值/可拷贝/可存储

有虚函数调用开销

Lambda

编译期生成匿名类型

无虚函数开销

可隐式转为 function

Lambda 的 operator() 是 const 的(除非用 mutable),且默认 constexpr(C++17)。无捕获 Lambda 还可隐式转换为函数指针。

四、面试题精讲

1. std::function 可以包装哪些类型?

自由函数、函数指针、Lambda、函数对象(重载 operator() 的类/结构体)、std::bind 表达式、成员函数指针(配合 std::mem_fn)、以及另一个 std::function。

2. SBO 失效的条件是什么?

可调用对象大小超过缓冲区(约 24–32 字节),或对齐要求超过 max_align_t,或对象具有 non-trivial 的析构函数导致无法原地缓冲。此时触发堆分配。

3. std::bind 和 Lambda 在参数转发上有什么区别?

std::bind 会按值存储绑定的实参,除非用 std::ref/cref,容易导致 dangling reference。Lambda 用 auto&& 捕获时可完美转发。C++ 标准建议从 C++14 开始优先使用 Lambda 而非 bind。

4. 为什么 Lambda 不是 std::function?

Lambda 是编译期具体类型,有确定的 operator() 签名,零虚函数开销。std::function 是运行时包装器,有类型擦除和间接调用开销。把 Lambda 赋值给 std::function 会触发类型擦除并损失性能。

5. 无捕获 Lambda 为什么能转为函数指针?

标准规定无捕获 Lambda 定义了一个 operator() 和一个隐式的转换函数 operator Ret(*)(Params...)()。这使得无捕获 Lambda 可直接作为 C API 的回调。

6. std::not_fn 与 std::not1/not2 的根本区别?

not1/not2 要求可调用对象提供 argument_type 等嵌套类型(适配器模式),且只能处理一元/二元谓词。not_fn 利用可变参数模板和完美转发,支持任意元数。

7. std::function 的空状态如何检测和重置?

f == nullptr 或 !f 判断空状态,f = nullptr 或 f = {} 清空。调用空 std::function 会抛出 std::bad_function_call 异常。

8. std::bind_front 和 std::bind 在语义上最重要的区别是什么?

std::bind 支持占位符重新排列参数次序;std::bind_front 只做前缀绑定,不支持占位符重排,语义最简单,编译更快。

C++ STL:unordered_map 自定义键值类型 本文在参考Mark Nelson的文章的基础上,提出了自己的样例,整理了完整的unordered_map自定义键值机制。 目录 1. 问题抛出 2. 三种解决方案 2.1 方法 1 - 指向函数的指针 2.2 方法 2 - 将哈希函数打包成函数对象 2.3 方法 3 - std::hashT的特化 1. 问题的抛出 首先,我们来看一下unordered_map的参数构成。 ... 阅读详情

相关推荐

MATLAB R2022b 安装教程

1. 下载安装先放资源链接,百度网盘太贵了不想续了,换了夸克网盘。1. 下载安装。

qq_51654158的博客 2万+

C++11详解(下)

C++11理解!!!

小八哥 1173

处理图数据, 安装“Pytorch“ + 图神经网络开源框架“PyG “+ “ogb“使用

"OGB" OSError: [WinError 127] 找不到指定的程序。1.[遇到这类问题往往跟torch安装的工具包(如torch-sparse,torch-scatter,torch-cluster)版本没对应上有关](https://blog.youkuaiyun.com/weixin_42358688/article/details/117433691)2.[OGB使用环境安装与依赖](https://ogb.stanford.edu/docs/home/)Data Loaders3.[torch-geo

Fuguir™的博客 3698

深入C++可调用对象:从function包装到bind参数适配的技术实现

若代码语言:javascriptAI代码解释ok,那现在,我们就可以实现将这些可调用对象放在一个容器里面了——代码语言:javascriptAI代码解释那如果我们需要包装的是普通成员变量或者静态成员变量呢?代码语言:javascriptAI代码解释ok,接下来,让我们来实战一下——

m0_74824865的博客 23

微信小程序视频内容服务类目审核不过?绕过审核的两种思路!

虽然不推荐绕过官方审核,但在实际项目中,总有一些特殊情况:比如,最近开发的小程序涉及视频播放,提交审核时,被官方要求补充“教育服务 - 在线视频课程”类目。但我们的内容只是车辆使用介绍,并非教育课程,无法提供相应资质。该怎么办?很多开发者发现,小程序开发不是最难的,审核才是最难的。 微信为了规避法律风险,要求某些功能必须提供资质。例如:然而,并不是所有公司都能轻松申请到这些资质,甚至有些资质根本不适用于我们的业务,导致审核卡住,无法上线。就像我遇到的这个问题,官方要求我们补充“在线视频课程”类目,但实际业务

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 3238

22、用户界面流程(UI Flow)全解析

本文全面解析了用户界面流程(UI Flow)的概念、创建步骤及其在系统设计中的应用。通过示例和操作步骤,详细介绍了如何确定屏幕范围、识别屏幕、创建过渡和标记触发器,并阐述了UI Flow在导航识别、验证、可用性优化、测试用例开发和需求推导中的重要作用。文章还结合问答系统案例,展示了UI Flow的实际应用价值,帮助读者深入理解并有效运用这一工具提升系统用户体验。

rice5的博客 153

【C++:C++11收尾】解构C++可调用对象:从入门到精通,掌握function包装器与bind适配器包装器详解

🎬 艾莉丝的简介:🎬 艾莉丝的C++专栏简介:Cplusplus.com​这个文档在C++98、C++11时候还行,之后就完全没法用了……——还 :C++准官方参考文档 这个行,包括C++26都同步了,我们以后主要会看这个。Standard C++ 这个网站上面会有很多大佬,类似于论坛。 8.1.2 概念 是一个类模板,也是一个包装器 的实例对象可以包装存储其他的可以调用对象,包括函数指针、仿函数、 、 等,存储的可调用对象被称为 的目标。若std::function不含目标,则称它为空。调用

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 2023

腾讯Android开发面试准备指南及参考答案

腾讯元宝高级Android开发职位面试准备指南 该职位要求扎实的Kotlin基础、架构设计能力和性能优化经验,重点关注Android客户端开发。面试流程约3轮,支持线上面试,流程较快。候选人需准备以下核心内容: 基础技术:Kotlin特性、Android开发生命周期、多线程编程 架构设计:MVVM/MVI模式应用、组件化解耦方案 性能优化:流畅度提升、内存管理、启动时间优化等实战案例 加分技能:Jetpack Compose开发、音视频处理、跨平台开发经验 建议结合项目经验准备具体案例,突出技术深度和问题解

郑伟强dev的专栏 1194

C++ 中 function、bind、lambda 详解:可调用对象的三剑客

Lambda 表达式是 C++11 引入的语法糖,用于在代码中就地定义匿名函数对象。它是编译器生成的、具有唯一类型的闭包对象。cpp复制下载// lambda 语法:[捕获列表](参数列表) -> 返回类型 { 函数体 }// 大多数情况可省略返回类型// 各种捕获方式// [=] : 按值捕获所有外部变量// [&] : 按引用捕获所有外部变量b++;c++;// 混合捕获// 初始化捕获(C++14):捕获表达式结果。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1182

Linux ARM平台开发系列讲解(GMSL摄像头篇)1.2 MAX9296 GMSL链路配置

串行器 +解串器组成,软件需要做的就是写这三部分的驱动,让摄像头数据能到达。3. 配置解串器和串行器的GMSL链路。串行器和解串器上电成功之后,就可以配置。软件配置(注意软件只可以配置速率,2. 9296上电,使能芯片。的版本和速率,比如版本是配置。配置成功之后,就会出现串行器。的链路,其它同类型的。设备地址,我设置的是。...

DSMGUOGUO的博客 1万+

Arduino开发实例-EMG 肌肉信号传感器

在医学研究中,测量肌肉的活动、收缩和扩张非常重要。 EMG 肌肉传感器测量肌肉活动并产生信号以显示扩张和收缩量。 因此,输出取决于所选肌肉的活动量。 3 绿色、红色和黄色电极连接到模块,用于传输来自肌肉运动的电信号。

视觉与物联智能 1696

【C++11】RAII 义体加装指南 ——【包装器 与 异常】C++11中什么是包装器?有哪些包装器?C++常见异常有哪些?(附带完整代码讲解)

本文介绍了C++11中的两个重要包装器工具:std::function和std::bind。std::function是一个通用的可调用对象包装器,能够统一处理函数指针、lambda表达式、仿函数等不同类型的可调用对象,实现类型擦除功能。文章通过代码示例展示了如何使用std::function包装各种类型的函数,并说明了其底层开销。 std::bind则是一个参数绑定器,可用于参数绑定、顺序调整和成员函数转换。文章详细介绍了bind的语法格式,并通过示例代码演示了参数绑定和参数顺序调整的用法

枫亭湖区的博客 1万+

【Frida Android】实战篇4:SSL Pinning 证书绑定绕过 Hook 教程(一)

在前两章中,我们学习了中间人抓包与 Hook 抓包的基础方法,这些方法在理想环境下能够有效获取网络请求数据。但在实际的企业级应用中,出于安全防护需求,很多 APP 会启用证书绑定(SSL Pinning)机制。这种机制会让客户端对服务器证书的校验更为严格,导致常规的抓包方法(即使将 BurpSuite/Charles 证书安装到系统信任区)也无法生效。因此,从本章开始,我们将系统学习 SSL Pinning 机制的绕过技术。SSL Pinning(证书绑定)是客户端校验服务器身份的进阶安全机制。

水滴石穿 1610

C++11 核心精髓:类新功能、lambda与包装器实战

C++11 不仅优化了泛型编程和性能(如移动语义、可变参数模板),还对类机制、可调用对象、资源管理进行了革命性升级。从类的默认函数控制、lambda 匿名函数,到 function/bind 包装器,再到智能指针,这些特性彻底改变了 C++ 的编程范式,让代码更简洁、安全、灵活。本文结合核心知识点和 test.cpp 代码,从类的新功能入手,逐步拆解 lambda 表达式、function/bind 包装器的用法与原理,帮你吃透 C++11 进阶特性,提升代码质量与开发效率。

会当凌绝顶,一览众山小 1435

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

《C++进阶之C++11》【lambda表达式 + 包装器】

本文介绍了C++中的lambda表达式和函数包装器的核心概念与用法。重点讲解了捕获列表的三种方式。文章通过代码示例详细演示了lambda的各种捕获方式和用法特点,适合C++进阶学习。

序属秋秋秋的博客 1656

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

C++进阶:(十四)C++11 深度解析(下):类功能、STL 升级、lambda 与包装器全解析

本文深入解析了C++11四大核心特性:1)类功能扩展:新增移动构造/赋值函数、成员变量缺省值、default/delete控制默认函数、final/override优化继承体系;2)STL升级:新增array/forward_list/unordered容器,提供移动语义接口、emplace系列高效操作;3)lambda表达式:实现灵活匿名函数,支持多种捕获方式,简化STL算法和线程编程;4)包装器:通过function统一可调用对象接口,bind实现参数适配。

2301_79248256的博客 1550

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

C++ std::function:统一可调用对象包装器的原理与应用

在C++编程中,回调机制和函数式编程是提升代码灵活性的关键技术。函数指针作为早期解决方案,虽然能实现回调,但存在声明复杂、类型严格、无法捕获上下文等局限。类型擦除技术通过统一接口包装不同具体类型,解决了这一问题,其核心价值在于提供通用的调用抽象。std::function正是基于此原理设计的通用可调用对象包装器,它能够容纳普通函数、lambda表达式、仿函数等多种实体,并提供了统一的调用接口。这一特性使其在事件系统、命令模式、任务队列等应用场景中极具优势,显著提升了代码的可维护性和表现力。通过理解std::

weixin_30319153的博客 442

C++之STL std::function及std::bind总结

bind第一个参数传入一个函数、函数指针、lamda表达式、类的成员函数都是可以的,这里注意,如果是类的成员函数,则需要再类的作用域前加上&,第二个占位符需要注意,如果不是类的成员函数,那么直接要么是一个参数,例如10,或者std::placeholders::_1(由调用bind返回值时传入),如果是类的成员函数,第二个参数是实例化的类对象,第三个参数开始才是函数的输入参数。也可参考std::function对于类的成员函数的调用。注:调用类成员函数时,需要和std::bind一起使用;

Lid_23的博客 2362

【STL十六】函数对象:包装器(std::function)——绑定器(std::bind)——函数适配器

包装器(std::function)——绑定器(std::bind)——函数适配器

junxuezheng的博客 1225

C++11 - std::function简要介绍以及可包装函数的几种形式总结

1 std::function 函数模板形式: template <class T>函数;//未定义 模板<class Ret,class ... Args> class function <Ret(Args ...)>; 1.1 函数模板说明 std::function是一个函数包装器模板,该函数包装器模板可以包装任意可以调用的元素,其包装器的类型只依赖于其调用特征,而不依赖于可调用元素自身的类型。 std::function可包装下列可调用元素类型: 函数 函数指

HW140701的博客 3821

C++11 std::function与std::bind:统一可调用对象与函数适配的终极指南

在C++编程中,回调机制和函数对象的灵活传递是构建模块化、可复用代码的关键。传统上,函数指针、仿函数、成员函数指针等不同类型的可调用对象(Callable Object)因类型系统割裂而难以统一处理,这限制了泛型编程和设计模式(如策略模式、命令模式)的实现。类型擦除(Type Erasure)技术为解决此问题提供了原理基础,它能够在运行时隐藏具体类型,仅保留统一的调用接口。C++11引入的std::function正是基于此原理的通用可调用对象包装器,它像一个万能插座,可以容纳任何签名匹配的可调用实体。而s

weixin_33226548的博客 359

C++11包装器std::function与std::bind深度解析:原理与实战

本文深入解析C++11中的std::function和std::bind工具。首先指出C++中存在多种可调用对象(函数指针、仿函数、Lambda等)但类型不统一的问题。std::function作为包装器,通过类型擦除技术实现了可调用对象的统一存储和调用,其底层基于模板和多态机制。std::bind作为函数适配器,能调整参数顺序、固定参数值。二者配合使用,可广泛应用于回调机制、策略模式等场景。文章详细介绍了从基本用法到底层实现的全方位知识,包括手写简易function来演示类型擦除原理,最后总结了这些工具在

2301_77799834的博客 318

[modern c++] std::function的使用

引言 c++ 11开始引入了对个可调用对象,比如 lambda,std::function,std::bind ,这些手段的目的只有一个,就是用来创建可调用对象,区别在于各自的适用场景不同,lambda用来创建一个临时可掉用对象,std::bind是一个函数,用来调整已有可调用对象的传参方式,并返回一个可调用对象。 std::function 是一个模板函数,可以用来包裹函数、成员函数、lambda或者可调用对象。 可以把std::function类比为一个函数指针,只不过这是一个可以实例化的指针。

ykun089的博客 1223

《C++11:bind绑定器与function包装器》

bind1st、bind2nd,C++11的bind及底层原理,C++11的function及底层原理,bind与function实现线程池。

2202_75924820的博客 760

C++ 的可调用对象

(3) lambda 表达式:就是匿名函数,普通的函数在使用前需要找个地方将这个函数定义,于是 C++提供了 lambda 表达式,需要函数时直接在需要的地方写一个 lambda 表达式,省去了定义函数的过程,增加开发效率。是一个通用的可调用对象包装器,可以存储任何可调用对象,包括普通函数、函数指针、函数对象和 lambda 表达式。(2) 仿函数:具有operator()函数的类对象,此时类对象可以当做函数使用,因此称为仿函数。(1) 函数:函数自然可以调用()运算符,是最典型的可调用对象。

chamu99的博客 914

C++11中的std::function

文章转载自:http://www.jellythink.com/archives/771 看看这段代码 先来看看下面这两行代码: std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed; std::function<void(EventKeyboard::KeyCode, Event*)> o...

大猪的博客 4421
上一篇: C++ STL 之 std::format 与 std::print 详解
下一篇: C++ STL之set/multiset详解:从使用到底层,再到面试八股
Ricky_Theseus
博客等级 码龄3年 126粉丝 158原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ricky_Theseus

感谢大家,祝您生活愉快

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值