
std::function是 C++11 引入的通用多态函数封装器 (定义在 <functional>头文件中),用于存储、复制和调用任意可调用对象(Callable Object)。
它解决了传统函数指针无法兼容 lambda、成员函数、函数对象等场景的痛点,是实现回调、策略模式、事件系统等设计的利器。
一、核心概念
std::function的本质是类型擦除(Type Erasure):它通过统一的接口隐藏了不同可调用对象的类型差异,允许你将任何符合签名的可调用对象"包装"成一个对象。其模板定义为:
cpp
template <class R, class... Args>
class function<R(Args...)>;
-
R:可调用对象的返回类型; -
Args...:可调用对象的参数类型列表(可变模板参数); -
整体表示"一个接受
Args...参数、返回R类型的可调用对象"。
二、基本用法
1. 声明与初始化
需指定目标函数的签名(返回类型 + 参数类型),然后用可调用对象初始化:
cpp
#include <functional>
#include <iostream>
// 示例1:普通函数
int add(int a, int b) { return a + b; }
// 示例2:函数对象(重载operator()的类)
struct Multiply {
int operator()(int a, int b) const { return a * b; }
};
int main() {
// 声明:接受两个int、返回int的可调用对象
std::function<int(int, int)> func;
// 初始化:普通函数
func = add;
std::cout << func(2, 3) << "\n"; // 输出5
// 初始化:lambda表达式(无捕获)
func = [](int a, int b) { return a - b; };
std::cout << func(5, 2) << "\n"; // 输出3
// 初始化:函数对象
Multiply mul;
func = mul;
std::cout << func(2, 3) << "\n"; // 输出6
return 0;
}
2. 支持的可调用对象类型
std::function几乎能包装所有符合签名的可调用对象,包括:
-
普通函数/静态成员函数 :直接用函数名或取地址(
&func); -
Lambda 表达式 :无论是否捕获变量(捕获的变量会被存储在
std::function内部); -
函数对象 (Functor):重载了
operator()的类实例; -
成员函数 :需用
std::bind或 lambda 绑定this指针; -
函数指针:传统的 C 风格函数指针。
示例:包装成员函数
成员函数需要绑定对象实例 (this指针)才能调用,常用 std::bind或 lambda 实现:
cpp
#include <functional>
#include <iostream>
class MyClass {
public:
int value = 10;
int method(int a) { return value + a; } // 非静态成员函数
static int static_method(int a) { return a * 2; } // 静态成员函数
};
int main() {
MyClass obj;
std::function<int(int)> func;
// 方式1:用std::bind绑定成员函数和对象
func = std::bind(&MyClass::method, &obj, std::placeholders::_1);
std::cout << func(5) << "\n"; // 输出15(10+5)
// 方式2:用lambda绑定(更直观)
func = [&obj](int a) { return obj.method(a); };
std::cout << func(5) << "\n"; // 输出15
// 静态成员函数(无需绑定对象)
func = &MyClass::static_method;
std::cout << func(5) << "\n"; // 输出10(5 * 2)
return 0;
}
3. 调用方式
std::function重载了 operator(),调用方式与普通函数完全一致:
cpp
std::function<int(int, int)> add_func = [](int a, int b) { return a + b; };
int result = add_func(3, 4); // 等价于调用lambda,result=7
若调用空的 std::function (未初始化或已被重置),会抛出 std::bad_function_call异常。因此建议调用前检查是否为空:
cpp
if (func) { // 或用 func != nullptr
func(1, 2);
}
三、关键特性
1. 空状态(Empty State)
-
默认构造的
std::function是空的(不指向任何可调用对象); -
可通过赋值
nullptr或调用reset()重置为空; -
用
operator bool()或== nullptr判断是否为空。
2. 拷贝与赋值
std::function满足值语义:
-
拷贝构造/赋值:复制一份可调用对象的"引用"(若函数对象或大 lambda 捕获了数据,可能触发堆分配);
-
移动构造/赋值:转移内部资源(避免不必要的拷贝)。
3. 类型擦除的开销
std::function的性能略低于直接调用或函数指针(因为类型擦除需要间接调用),但在大多数场景(如回调、事件处理)中可忽略。极端性能敏感场景(如嵌入式高频调用)需权衡是否使用。
四、典型使用场景
1. 回调函数(Callback)
异步操作(如网络请求、硬件中断)完成后执行的逻辑,用 std::function封装回调:
cpp
// 模拟异步任务:完成后调用回调
void async_task(std::function<void(int)> callback) {
int result = 42; // 模拟任务结果
callback(result); // 触发回调
}
int main() {
// 用lambda作为回调
async_task([](int res) {
std::cout << "Task done: " << res << "\n"; // 输出42
});
return 0;
}
2. 事件处理系统
注册多个事件处理器(如按键、网络消息),用 std::function统一管理:
cpp
#include <vector>
#include <functional>
class EventSystem {
public:
using Handler = std::function<void(int)>; // 事件处理器类型:接受int参数
void register_handler(Handler h) {
handlers_.push_back(h);
}
void trigger_event(int data) {
for (auto& h : handlers_) {
if (h) h(data); // 调用所有注册的 handler
}
}
private:
std::vector<Handler> handlers_;
};
int main() {
EventSystem es;
// 注册两个处理器:打印数据和翻倍数据
es.register_handler([](int d) { std::cout << "Data: " << d << "\n"; });
es.register_handler([](int d) { std::cout << "Double: " << d*2 << "\n"; });
es.trigger_event(10); // 输出 Data:10 和 Double:20
return 0;
}
3. 策略模式(Strategy Pattern)
将算法封装为 std::function,动态切换策略:
cpp
using SortStrategy = std::function<void(std::vector<int>&)>;
void bubble_sort(std::vector<int>& v) { /* 冒泡排序 */ }
void quick_sort(std::vector<int>& v) { /* 快速排序 */ }
void sort(std::vector<int>& v, SortStrategy strategy) {
strategy(v); // 用传入的策略排序
}
int main() {
std::vector<int> nums = {3,1,2};
sort(nums, bubble_sort); // 用冒泡排序
sort(nums, quick_sort); // 用快速排序
return 0;
}
五、注意事项
-
空调用异常 :调用空的
std::function会抛std::bad_function_call,务必先检查非空; -
性能开销:类型擦除导致间接调用,高频场景需测试性能;
-
捕获变量的生命周期 :若 lambda 捕获了局部变量,需确保变量生命周期长于
std::function(否则会悬垂引用); -
大小问题 :
std::function的大小通常大于函数指针(可能包含堆分配的指针),需注意内存布局(如嵌入式系统的固定大小需求)。
六、与函数指针的对比
| 特性 | 函数指针 | std::function |
|---|---|---|
| 支持的类型 | 仅普通函数/静态成员函数 | 所有可调用对象(lambda、成员函数、函数对象等) |
| 捕获上下文 | 不支持 | 支持(lambda 捕获、成员函数绑定 this) |
| 空状态处理 | 用 NULL 表示 | 用 empty() 或 operator bool() |
| 性能 | 直接调用(快) | 间接调用(略慢) |
总结
std::function是 C++ 中统一可调用对象接口 的核心工具,极大提升了代码的灵活性和扩展性。在嵌入式 Linux 开发中,常用于驱动回调、事件处理、异步任务 等场景,配合 lambda 和 std::bind能大幅简化代码。
如果需要进一步优化性能(如嵌入式高频调用),可考虑用模板 替代 std::function(编译期确定类型),但会失去灵活性------需根据场景权衡。
