std::functional 使用场景

std::functional 是 C++ 标准库中一个非常强大的工具,它提供了一种**类型擦除(type erasure)**机制,让你能够存储、传递和调用任何可调用对象(callable)。

核心作用

1. 统一的可调用对象包装器

std::function 可以包装任何可调用实体,只要签名匹配:

  • 普通函数
  • Lambda 表达式
  • 函数对象(仿函数)
  • 成员函数(通过 std::bind 或 lambda)
  • 甚至其他 std::function

2. 类型擦除

隐藏具体类型,只暴露接口。这使得你可以:

  • 在容器中存储不同类型的可调用对象
  • 作为函数参数接受任意可调用对象
  • 实现回调机制而不需要模板

主要使用场景

场景 1:回调函数(Callbacks)

cpp 复制代码
#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();

场景 2:策略模式 / 算法注入

cpp 复制代码
#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; }         // 平方
);

场景 3:事件系统 / 观察者模式

cpp 复制代码
#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"; 
});

场景 4:延迟执行 / 任务队列

cpp 复制代码
#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();

场景 5:绑定成员函数

cpp 复制代码
#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 vs 模板 vs 裸函数指针

特性 std::function 模板 裸函数指针
类型擦除 ✅ 是 ❌ 否 ✅ 是(但只能指向函数)
存储 Lambda ✅ 可以 ✅ 可以 ❌ 不行(除非无捕获)
运行时开销 有(虚函数调用)
编译时类型检查 弱(运行时可能抛 bad_function_call
存储在容器中 ✅ 容易 ❌ 难(需要类型擦除) ✅ 可以

最佳实践

  1. 优先使用模板:如果不需要存储可调用对象,用模板参数更高效

    cpp 复制代码
    // 更好:零开销
    template<typename Func>
    void execute(Func&& f) { f(); }
    
    // 有开销:类型擦除
    void execute(std::function<void()> f) { f(); }
  2. 检查空状态 :调用前确保 std::function 不为空

    cpp 复制代码
    if (func) func();  // 或 if (func != nullptr)
  3. 注意开销std::function 通常使用小对象优化(SOO),但大对象会堆分配

  4. C++23 替代 :考虑使用 std::move_only_function(仅移动)或 std::copyable_function

std::functional 的核心价值在于灵活性------当你需要在运行时决定调用什么、或者需要在容器中存储可调用对象时,它是不可或缺的工具。

相关推荐
hetao17338371 小时前
2026-02-09~02-12 hetao1733837 的刷题记录
c++·算法
ADDDDDD_Trouvaille2 小时前
2026.2.12——OJ72-74题
c++·算法
梵刹古音2 小时前
【C++】函数重写
开发语言·c++
Titan20242 小时前
C++异常学习笔记
c++·笔记·学习
柒儿吖3 小时前
DDlog 高性能异步日志库在 OpenHarmony 的 lycium 适配与分步测试
c++·c#·openharmony
民国二十三画生3 小时前
C++(兼容 C 语言) 的标准输入语法,用来读取一行文本
c语言·开发语言·c++
柒儿吖3 小时前
基于 lycium 在 OpenHarmony 上交叉编译 utfcpp 完整实践
c++·c#·harmonyos
sTone873753 小时前
std::function/模板/裸函数指针选型指南
c++
无聊的小坏坏3 小时前
一文讲通:二分查找的边界处理
数据结构·c++·算法