【C++】std::function是什么
std::function是C++11引入的一个模板类,它可以看作是一个"万能函数容器",能够存储、复制和调用几乎任何可调用的目标(函数、lambda表达式、函数对象等)。
一、std::function是什么?
- 想象你有一个神奇的盒子(std::function),这个盒子可以装下:
- 普通函数
- 类的成员函数
- lambda表达式
- 函数对象(重载了operator()的类)
这个盒子不仅能保存这些可调用对象,还能像普通函数一样使用它们。
二、std::function的基本用法
- 基本语法
#include <iostream>
#include <functional>// 普通函数
int add(int a, int b) {return a + b;
}int main() {// 创建一个可以存储"接受两个int参数,返回int"的函数的包装器std::function<int(int, int)> func;// 存储普通函数func = add;std::cout << func(2, 3) << std::endl; // 输出5// 存储lambda表达式func = [](int a, int b) { return a * b; };std::cout << func(2, 3) << std::endl; // 输出6return 0;
}
三、std::function的作用
统一接口:可以用相同的方式调用不同类型的可调用对象
延迟执行:先保存函数,稍后再调用
回调机制:常用于事件处理、观察者模式等场景
运行时多态:可以在运行时决定调用哪个函数
1. 设置lambda函数为回调函数
#include <functional>
#include <vector>class Button {std::function<void()> onClick;
public:void setOnClick(std::function<void()> callback) {onClick = callback;}void click() {if(onClick) onClick(); // 如果有回调函数就调用}
};int main() {Button btn;// 设置lambda作为回调btn.setOnClick([]() {std::cout << "按钮被点击了!" << std::endl;});btn.click(); // 输出"按钮被点击了!"return 0;
}
2. 策略模式
#include <functional>
#include <vector>class Sorter {std::function<bool(int, int)> compare;
public:void setCompare(std::function<bool(int, int)> cmp) {compare = cmp;}void sort(std::vector<int>& nums) {if(compare) {// 使用设置的比较函数排序std::sort(nums.begin(), nums.end(), compare);} else {// 默认排序std::sort(nums.begin(), nums.end());}}
};int main() {Sorter sorter;std::vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};// 设置升序排序策略sorter.setCompare([](int a, int b) { return a < b; });sorter.sort(nums);// 设置降序排序策略sorter.setCompare([](int a, int b) { return a > b; });sorter.sort(nums);return 0;
}
3. 存储成员函数
#include <functional>class Calculator {
public:int multiply(int a, int b) {return a * b;}
};int main() {Calculator calc;// 存储成员函数需要绑定对象实例std::function<int(int, int)> func = std::bind(&Calculator::multiply, &calc, std::placeholders::_1, std::placeholders::_2);std::cout << func(3, 4) << std::endl; // 输出12return 0;
}
四、总结
什么时候使用std::function?
- 当你需要存储不同类型的可调用对象时
- 当你需要在运行时决定调用哪个函数时
- 当你需要实现回调机制时
- 当你需要统一函数接口时
疑问
1. 为什么回调函数常用Lambda而非普通函数
void processOrder(const std::string& dish) {int priority = getCurrentPriority(); // 获取当前优先级// Lambda可以"记住"当前priority的值kitchen.notify([priority, &dish]() {std::cout << "制作" << dish << " (优先级:" << priority << ")";});
}
- 普通函数无法直接访问局部变量priority
- Lambda可以捕获当前作用域的变量,形成闭包
- Lambda把逻辑放在使用的地方,减少跳转
- 特别适合简单的一次性操作
- 如果使用函数指针作为回调函数的形参,那么传入lambda表达式的时候不能有捕获参数;如果lambda有捕获参数,想要作为形参传给回调函数,就只能使用std::function进行封装。
2. 虽然Lambda很强大,但普通函数仍有其用途
- 复杂逻辑:当回调逻辑很复杂时,单独的函数更易维护
- 需要重用时:多个地方调用相同逻辑时
- C接口兼容:需要转换为函数指针时