news 2026/4/14 20:46:14

C++ 可调用对象包装器:function 与 bind

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 可调用对象包装器:function 与 bind

一、什么是可调用对象?

在 C++ 中,“可调用对象”指的是可以像函数一样被调用的对象。主要有以下几种:

类型示例
函数指针void (*p)()
仿函数重载了operator()的类对象
Lambda 表达式[](int x){ return x; }
类成员函数指针&MyClass::func
std::function 包装的对象std::function<void()> f = func;

二、函数指针

函数指针是最传统的可调用对象,本质是指向函数的指针

#include <iostream> using namespace std; int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int main() { // 定义函数指针:返回值 (*指针名)(参数类型) int (*pf)(int, int) = nullptr; // 指向 add 函数 pf = add; // 函数名就是地址,等价于 pf = &add; cout << pf(10, 20) << endl; // 30 // 指向 sub 函数 pf = sub; cout << pf(10, 20) << endl; // -10 return 0; }

说明:函数名就是函数的地址,所以pf = addpf = &add都可以。函数指针可以指向不同类型的函数,只要返回值类型和参数列表匹配。

三、仿函数

仿函数是一个重载了operator()的类或结构体,它的对象可以像函数一样调用。

class Add { private: int value; public: Add() : value(0) {} int operator()(int a, int b) { value = a + b; return value; } int operator()(int a) { value = a * a; return value; } void Print() const { cout << "value: " << value << endl; } }; int main() { Add add; int c = add(10, 20); // 调用 operator()(int, int) // 等价于 add.operator()(10, 20) add.Print(); // value: 30 c = add(5); // 调用 operator()(int) // 等价于 add.operator()(5) add.Print(); // value: 25 return 0; }

为什么add(10, 20)能像函数一样调用?

因为Add类重载了operator()add(10, 20)是语法糖,编译器会转换成add.operator()(10, 20)。这让对象可以像函数一样被调用,所以称为“仿函数”。

特点

特点说明
可以有多个重载版本参数不同,调用时自动匹配
可以保存状态有成员变量,多次调用可以记住之前的值
可以作为算法的参数sort的自定义比较函数

四、std::function 可调用对象包装器

问题:为什么需要 std::function?

不同类型的可调用对象,类型不一样:

可调用对象类型
普通函数void(*)()
仿函数Foo(自定义类型)
Lambda编译器生成的唯一类型(无法写出)
void func() {} struct Foo { void operator()() {} }; auto lam = []() {};

三个变量类型不同,无法放在同一个数组里

std::function 的作用:把不同类型的可调用对象,包装成统一的类型

std::function<void()> f; // 统一类型 f = func; // 可以存普通函数 f = Foo(); // 可以存仿函数 f = [](){}; // 可以存 Lambda

4.1 基本用法

#include <functional> #include <iostream> using namespace std; void func() { cout << "全局函数" << endl; } struct Foo { void operator()() { cout << "仿函数" << endl; } }; struct Bar { static void static_func() { cout << "静态成员函数" << endl; } }; int main() { // std::function<void(void)> 表示:包装一个返回 void、无参数的函数 std::function<void(void)> f; f = func; // 包装全局函数 f(); // 输出:全局函数 f = Bar::static_func; // 包装静态成员函数 f(); // 输出:静态成员函数 Foo foo; f = foo; // 包装仿函数 f(); // 输出:仿函数 return 0; }

语法解释std::function<void(void)>中的void(void)表示:

  • 第一个void:返回值类型

  • 括号内的void:参数列表(无参数)

std::function<void(void)>表示:包装一个返回 void、无参数的函数

4.2 为什么需要 std::function?

场景:想把多个不同类型的可调用对象放进一个数组,统一调用。

std::function<void(void)> funcList[3]; funcList[0] = func; // 普通函数 funcList[1] = foo; // 仿函数 funcList[2] = []() { cout << "Lambda" << endl; }; // Lambda for (auto& f : funcList) { f(); // 统一调用 }

没有 std::function 的话:三种类型不同,无法放在同一个数组里。

一句话总结

std::function是一个“万能容器”,可以把函数指针、仿函数、Lambda 等统一装进去,然后统一调用。

五、std::bind 绑定器

std::bind用于将函数的参数绑定到固定值,生成一个新的可调用对象。

5.1 基本用法

#include <functional> #include <iostream> using namespace std; void output(int a, int b) { cout << "a = " << a << ", b = " << b << endl; } int main() { int x = 10, y = 20; // 两个参数都固定 auto f1 = std::bind(output, x, y); f1(); // 输出:a = 10, b = 20 return 0; }

说明std::bind(output, x, y)output的两个参数都固定为xy,生成的新函数f1不需要再传参。

5.2 占位符(_1_2

占位符表示参数在调用时才传入。

#include <functional> using namespace std::placeholders; // _1, _2 在这个命名空间 int main() { int x = 10; // 第一个参数固定,第二个参数调用时传入 auto f2 = std::bind(output, x, _1); f2(12); // a = 10, b = 12 // 第一个参数固定,第二个参数用占位符 _2(只绑定第二个实参) auto f3 = std::bind(output, x, _2); f3(1, 2); // a = 10, b = 2(_2 取第二个参数 2) // 参数顺序交换 auto f4 = std::bind(output, _2, _1); f4(1, 2); // a = 2, b = 1(_2 取第二个参数,_1 取第一个参数) return 0; }

说明_1表示调用时的第一个参数,_2表示第二个参数。通过占位符可以固定部分参数,或重新排列参数顺序。

5.3 占位符说明

占位符含义
_1调用时的第一个参数
_2调用时的第二个参数
_3调用时的第三个参数
// bind 时定义参数顺序,调用时传入实参 auto f = std::bind(output, _2, _1, _3); f(10, 20, 30); // 实际调用 output(20, 10, 30)

说明:占位符可以任意重排参数顺序,_1永远是调用时的第一个参数,_2是第二个,以此类推。

5.4 绑定成员函数

class Int { private: int value; public: Int(int x = 0) : value(x) {} void print() const { cout << "value = " << value << endl; } }; int main() { Int a(10); // 绑定成员函数需要传递对象指针 std::function<void(void)> f = std::bind(&Int::print, &a); f(); // 输出:value = 10 return 0; }

说明:成员函数有一个隐式的this参数,绑定时需要传递对象指针(&a)。

5.5 bind 的作用:统一成无参函数

void output(int a, int b, int c) { cout << a << ", " << b << ", " << c << endl; } int main() { // 把三个参数都固定,变成无参函数 auto f = std::bind(output, 1, 2, 3); f(); // 输出:1, 2, 3 return 0; }

核心优势:可以把任何带参数的函数转换成无参函数,方便存储到容器或作为回调函数。

六、完整示例

下面的代码综合演示了std::functionstd::bind的用法。

#include <iostream> #include <functional> using namespace std; using namespace std::placeholders; // 1. 普通函数 int add(int a, int b) { return a + b; } // 2. 仿函数 struct Multiply { int operator()(int a, int b) const { return a * b; } }; // 3. 成员函数 class Calculator { public: int divide(int a, int b) const { return a / b; } }; int main() { // std::function 统一存储不同类型的可调用对象 std::function<int(int, int)> func; // 存储函数指针 func = add; cout << "add: " << func(10, 20) << endl; // 30 // 存储仿函数 Multiply mul; func = mul; cout << "multiply: " << func(10, 20) << endl; // 200 // 存储 Lambda func = [](int a, int b) { return a - b; }; cout << "lambda: " << func(10, 20) << endl; // -10 // std::bind 绑定成员函数 Calculator calc; func = std::bind(&Calculator::divide, &calc, _1, _2); cout << "divide: " << func(20, 10) << endl; // 2 // bind 固定部分参数 auto fixed = std::bind(add, 100, _1); cout << "fixed: " << fixed(50) << endl; // 150 return 0; }

示例说明

  • std::function<int(int, int)>定义了一个统一类型:接收两个int,返回int

  • 函数指针、仿函数、Lambda 都可以赋值给func

  • std::bind可以将成员函数或普通函数绑定成新的可调用对象

七、总结

知识点核心要点
函数指针指向函数的指针,int (*pf)(int, int)
仿函数重载operator()的类对象,add(10,20)实际是add.operator()(10,20)
仿函数特点可保存状态、可重载、可作算法参数
std::function通用可调用对象包装器,统一存储函数指针、仿函数、Lambda
std::bind绑定函数参数,生成新可调用对象
占位符_1_2_3,表示调用时传入的参数位置
绑定成员函数需要传递对象指针(&obj),因为有隐式this
bind 作用把带参函数转成无参函数,方便存储到容器或作为回调

一句话总结

std::function统一存储各类可调用对象,std::bind绑定参数生成新函数,配合占位符可重排参数顺序。仿函数通过operator()让对象像函数一样调用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 20:45:10

微信聊天记录永久备份终极指南:三步完成数据导出与离线查看

微信聊天记录永久备份终极指南&#xff1a;三步完成数据导出与离线查看 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否担心更换手机时丢失珍贵的聊天记录&#xf…

作者头像 李华
网站建设 2026/4/14 20:30:14

经典标识TAG

import turtle import matht turtle.Turtle() t.speed(0) t.hideturtle()# 画布设置&#xff0c;让图形居中 screen turtle.Screen() screen.setup(width800, height800) screen.bgcolor("white") screen.title("等距嵌套三角形")def draw_triangle(size…

作者头像 李华
网站建设 2026/4/14 20:26:03

25道Shell笔试题

1、 用sed修改test.txt的23行test为tset&#xff1b; sed –i ‘23s/test/tset/g’ test.txt2、 查看/web.log第25行第三列的内容。 sed –n ‘25p’ /web.log | cut –d “ ” –f3 head –n25 /web.log | tail –n1 | cut –d “ ” –f3 awk –F “ ” ‘NR23{print $…

作者头像 李华
网站建设 2026/4/14 20:25:56

低功耗数据采集终端:无线传输,摆脱有线布线限制

一、核心定义 低功耗数采仪(低功耗数据采集终端/RTU)&#xff0c;是专为无市电、野外无人值守、长期独立运行场景设计的物联网数据采集与无线传输终端&#xff0c;核心是超低功耗电池/太阳能供电定时采集上传工业级稳定&#xff0c;解决传统数采仪功耗高、无法无源长期工作的痛…

作者头像 李华
网站建设 2026/4/14 20:24:10

Mem0 vs 传统记忆系统:为什么AI Agent需要持久化记忆?

Mem0 vs 传统记忆系统&#xff1a;为什么AI Agent需要持久化记忆&#xff1f; 想象一下&#xff0c;当你走进常去的咖啡店&#xff0c;店员不仅记得你喜欢的饮品&#xff0c;还能根据天气推荐今天的特调——这种个性化服务建立在持续记忆的基础上。而今天的AI Agent&#xff0…

作者头像 李华