std::functional 是 C++ 标准库中一个非常强大的工具,它提供了一种**类型擦除(type erasure)**机制,让你能够存储、传递和调用任何可调用对象(callable)。
std::function 可以包装任何可调用实体,只要签名匹配:
隐藏具体类型,只暴露接口。这使得你可以:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <functional> #include <iostream>
class Button { std::function<void()> onClick_; public: void setOnClick(std::function<void()> callback) { onClick_ = callback; } void click() { if (onClick_) onClick_(); } };
// 使用 Button btn; btn.setOnClick([]() { std::cout << "Clicked!\n"; }); btn.click(); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <functional> #include <vector> #include <algorithm>
void processData(std::vector<int>& data, std::function<bool(int)> filter, std::function<int(int)> transform) { // 先过滤 data.erase(std::remove_if(data.begin(), data.end(), [&](int x) { return !filter(x); }), data.end()); // 再转换 for (auto& x : data) x = transform(x); }
// 使用 std::vector<int> nums = {1, 2, 3, 4, 5, 6}; processData(nums, [](int x) { return x % 2 == 0; }, // 只保留偶数 [](int x) { return x * x; } // 平方 ); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <functional> #include <vector> #include <string>
class EventSystem { std::vector<std::function<void(const std::string&)>> listeners_; public: void subscribe(std::function<void(const std::string&)> listener) { listeners_.push_back(listener); } void emit(const std::string& event) { for (auto& listener : listeners_) { listener(event); } } };
// 使用 EventSystem events; events.subscribe([](const std::string& e) { std::cout << "Logger: " << e << "\n"; }); events.subscribe([](const std::string& e) { std::cout << "Metrics: recorded " << e << "\n"; }); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <functional> #include <queue> #include <iostream>
class TaskQueue { std::queue<std::function<void()>> tasks_; public: void addTask(std::function<void()> task) { tasks_.push(task); } void runAll() { while (!tasks_.empty()) { tasks_.front()(); tasks_.pop(); } } };
// 使用 TaskQueue queue; queue.addTask([]() { std::cout << "Task 1\n"; }); queue.addTask([]() { std::cout << "Task 2\n"; }); queue.runAll(); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <functional> #include <iostream>
class Calculator { public: int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; } };
// 使用 std::bind Calculator calc; auto addFunc = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2); std::cout << addFunc(3, 4); // 7
// 或者使用 lambda(更推荐,性能更好) auto multiplyFunc = [&calc](int a, int b) { return calc.multiply(a, b); }; |
| 特性 | std::function | 模板 | 裸函数指针 |
|---|---|---|---|
| 类型擦除 | ? 是 | ? 否 | ? 是(但只能指向函数) |
| 存储 Lambda | ? 可以 | ? 可以 | ? 不行(除非无捕获) |
| 运行时开销 | 有(虚函数调用) | 无 | 无 |
| 编译时类型检查 | 弱(运行时可能抛 bad_function_call) | 强 | 强 |
| 存储在容器中 | ? 容易 | ? 难(需要类型擦除) | ? 可以 |
优先使用模板:如果不需要存储可调用对象,用模板参数更高效
|
1 2 3 4 5 6 |
// 更好:零开销 template<typename Func> void execute(Func&& f) { f(); }
// 有开销:类型擦除 void execute(std::function<void()> f) { f(); } |
检查空状态:调用前确保 std::function 不为空
|
1 |
if (func) func(); // 或 if (func != nullptr) |
注意开销:std::function 通常使用小对象优化(SOO),但大对象会堆分配
C++23 替代:考虑使用 std::move_only_function(仅移动)或 std::copyable_function
std::functional 的核心价值在于灵活性——当你需要在运行时决定调用什么、或者需要在容器中存储可调用对象时,它是不可或缺的工具。