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

function bind

1. std::function:万能的可调用对象包装器

在 C++11 之前,如果你想把一个“函数”作为参数传递,你可能需要处理多种令人头大的类型:

  1. 普通函数指针 (void (*)(int))
  2. 仿函数(Functor) (重载了 operator() 的类对象)
  3. 成员函数指针
  4. Lambda 表达式 (C++11 引入,每个 Lambda 都有独特的匿名类型)

std::function 的出现,统一了所有这些“可调用对象(Callable)”的类型

核心用法

它是一个类模板,语法为 std::function<返回值(参数类型)>

#include <iostream>
#include <functional>// 1. 普通函数
void print_num(int i) { std::cout << "Function: " << i << std::endl; }// 2. 仿函数
struct PrintNum {void operator()(int i) const { std::cout << "Functor: " << i << std::endl; }
};int main() {// 声明一个能接受 int,返回 void 的通用包装器std::function<void(int)> func;// A. 包装普通函数func = print_num;func(10);// B. 包装 Lambdafunc = [](int i) { std::cout << "Lambda: " << i << std::endl; };func(20);// C. 包装仿函数func = PrintNum();func(30);return 0;
}

底层原理:类型擦除 (Type Erasure)

你可能会好奇:std::function 怎么能同时装下函数指针(4字节或8字节)和一整个对象(大小不固定)呢?

答案是 类型擦除

  1. 继承与虚表: std::function 内部通常持有一个基类指针,指向一个包含实际可调用对象的派生类。通过虚函数调用实际的代码。
  2. 堆内存分配: 如果存储的对象较小(如函数指针),它会利用 SBO (Small Buffer Optimization) 直接存在栈上;如果对象较大(如捕获了大量变量的 Lambda),它会在堆(Heap)上分配内存来存储该对象。
  3. 性能开销: 调用 std::function 相比直接调用函数指针,会有一次虚函数调用的开销(以及可能的堆分配开销)。因此,在极致性能敏感的热点代码路径(Hot Path)中,要谨慎使用。

2. std::bind:函数适配器(胶水)

std::bind 的作用是将一个可调用对象的参数绑定(固定)住,或者重新排列参数顺序,从而生成一个新的可调用对象。

这在计算机科学中被称为 偏函数应用 (Partial Application)

核心用法

你需要配合 std::placeholders(占位符)使用。

#include <iostream>
#include <functional>using namespace std::placeholders; // 引入 _1, _2 等占位符void add(int a, int b, int c) {std::cout << a << " + " << b << " + " << c << " = " << (a + b + c) << std::endl;
}int main() {// 场景 1: 固定参数(降维)// 把 add 的第一个参数固定为 10,第二个参数固定为 20,只需要传第三个参数auto bind_func1 = std::bind(add, 10, 20, _1);bind_func1(5); // 相当于调用 add(10, 20, 5) -> 输出 35// 场景 2: 改变参数顺序// 新函数的第一个参数传给原函数的第二个,新函数的第二个传给原函数的第一个auto bind_func2 = std::bind(add, _2, _1, 100);bind_func2(5, 8); // 相当于调用 add(8, 5, 100) -> 输出 113return 0;
}

3. 深度对比:std::bind 还是 Lambda?

这是现代 C++ 开发中非常关键的一个判断点。

结论先行:在 C++11/14 之后,尽量使用 Lambda 表达式,少用或不用 std::bind

虽然图片里提到了 bind,但它在现代 C++ 中已经稍显过时了。原因如下:

A. 可读性

  • Bind: 代码晦涩,需要理解 _1, _2 这种占位符的映射关系。
  • Lambda: 代码直观,逻辑清晰。
// 假设有一个类方法: void Hero::attack(int damage, int range);// 使用 bind (你需要处理 this 指针)
auto attack_50 = std::bind(&Hero::attack, hero_instance, 50, _1);// 使用 Lambda (清晰明了)
auto attack_50 = [hero_instance](int range) { hero_instance->attack(50, range); 
};

B. 性能

  • Bind: 通常会带来更多的模板实例化开销,且编译器较难优化(因为它存储的是参数的拷贝或引用,层层包装)。
  • Lambda: 编译器可以将其直接内联(Inline),通常也就是生成一个匿名类,性能几乎等同于手写代码。

C. 参数求值顺序

std::bind 对参数的求值行为可能不直观(所有参数在 bind 时被复制或移动),而 Lambda 的捕获列表([&][=])让你能精确控制是传值还是传引用。


4. 总结与应用场景

图片中的知识点属于 C++ 函数式编程的基础设施。

  1. std::function接口
    • 何时用: 当你需要设计一个回调函数(Callback)接口,或者要在容器(如 vector)里存不同类型的函数时。它是多态的。
    • 代价: 运行时开销(虚调用 + 内存分配)。
  2. std::bind适配器
    • 何时用: 在维护老旧代码(C++11 早期)时可能会遇到。
    • 现代替代品: 在新代码中,请优先使用 Lambda 表达式。Lambda 更快、更易读、更强大
http://www.jsqmd.com/news/115923/

相关文章:

  • 日记12,19
  • Item10--令赋值操作符返回一个
  • Item9--绝不在构造和析构过程中调用虚函数
  • python django flask考研互助交流平台_c62p51fu--论文
  • 日记12.18
  • 离散化遍历
  • Ubuntu上使用VScode创建Maven项目
  • 线程(2)
  • 大规模语言模型的抽象思维与创新能力培养
  • 线程(1)
  • 方达炬〖发明超新技术〗:冰堆技术;冷极冰堆建筑技术;
  • Item6--若不想使用编译器自动生成的函数,就该明确拒绝
  • 我发现LLM解析基因数据优化抗癌药剂量,患者副作用直降40%
  • 日记12.16
  • 论文AIGC查重率高怎么办?6个降AI率工具和技巧,AI率从100%降到3%! - 还在做实验的师兄
  • PCL曲面重建——为一组点云重建凸多边形/凹多边形
  • 信息与关系:涌现的三大核心原则
  • Linux文件权限
  • 28
  • 灵遁者:量子基元理论带来的新观点
  • 【补充】远程连接学校服务器操作说明
  • 本地私有知识库新选择:访答软件真实体验分享
  • 划分dp
  • 花边服饰银发红眸者山间近景
  • 日记12,15
  • Item4--确定对象被使用前已先被初始化
  • string_view
  • 当K3s遇见RustFS:轻量级边缘存储方案的探索与实践
  • 比话降AI靠谱吗?比话能降知网AI率吗? - 还在做实验的师兄
  • 树形背包