C++11 std::function,10分钟讲清楚

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;
}

五、注意事项

  1. 空调用异常 :调用空的 std::function会抛 std::bad_function_call,务必先检查非空;

  2. 性能开销:类型擦除导致间接调用,高频场景需测试性能;

  3. 捕获变量的生命周期 :若 lambda 捕获了局部变量,需确保变量生命周期长于 std::function(否则会悬垂引用);

  4. 大小问题std::function的大小通常大于函数指针(可能包含堆分配的指针),需注意内存布局(如嵌入式系统的固定大小需求)。

六、与函数指针的对比

特性 函数指针 std::function
支持的类型 仅普通函数/静态成员函数 所有可调用对象(lambda、成员函数、函数对象等)
捕获上下文 不支持 支持(lambda 捕获、成员函数绑定 this)
空状态处理 用 NULL 表示 用 empty() 或 operator bool()
性能 直接调用(快) 间接调用(略慢)

总结

std::function是 C++ 中统一可调用对象接口 的核心工具,极大提升了代码的灵活性和扩展性。在嵌入式 Linux 开发中,常用于驱动回调、事件处理、异步任务 等场景,配合 lambda 和 std::bind能大幅简化代码。

如果需要进一步优化性能(如嵌入式高频调用),可考虑用模板 替代 std::function(编译期确定类型),但会失去灵活性------需根据场景权衡。

相关推荐
leaves falling2 小时前
C++入门基础
开发语言·c++
huaweichenai2 小时前
java的数据类型介绍
java·开发语言
你真是饿了2 小时前
10.list
c++·list
tankeven2 小时前
HJ139 小红的01子序列计数(hard)
c++·算法
weixin_649555672 小时前
C语言程序设计第四版(何钦铭、颜晖)第十章函数与程序设计之汉诺塔问题
c语言·c++·算法
C羊驼2 小时前
C语言:随机数
c语言·开发语言·经验分享·笔记·算法
fengfuyao9852 小时前
CH552多功能音量调节旋钮设计与实现
c语言·开发语言
xushichao19892 小时前
实时数据压缩库
开发语言·c++·算法
minji...2 小时前
Linux 文件系统 (三) 软连接和硬链接
linux·运维·服务器·c++·算法