C++ std::function:万能函数包装器

在 C++ 中,std::function 是一个极其强大且灵活的通用多态函数包装器 。它定义在 <functional> 头文件中,能够存储、复制和调用任何可调用目标(Callable Target)。


1. 核心定义与语法

std::function 的模板参数是一个函数签名 ,格式为 返回值类型(参数列表)。

C++

复制代码
#include <functional>

// 声明一个返回值为 int,接受两个 int 参数的函数包装器
std::function<int(int, int)> func;

2. 它可以包装哪些对象?

std::function 的魅力在于它的"杂食性",它可以统一处理以下所有类型:

  • 普通函数

  • Lambda 表达式

  • 函数对象(重载了 operator() 的类实例)

  • 类成员函数 (需配合 std::bind 或 Lambda)

  • 类静态成员函数


3. 基本用法示例

包装普通函数与 Lambda

C++

复制代码
int add(int a, int b) { return a + b; }

std::function<int(int, int)> f;

f = add;               // 普通函数
f = [](int a, int b) { // Lambda
    return a * b; 
};

包装类成员函数

包装成员函数时,必须指明它是哪个对象的成员。通常有两种方式:

  1. 使用 std::bind

  2. 使用 Lambda(推荐)

C++

复制代码
struct Foo {
    void print_add(int i) const { std::cout << i << '\n'; }
};

Foo foo;
// 方式 1:Lambda
std::function<void(int)> f1 = [&](int i) { foo.print_add(i); };

// 方式 2:std::bind
std::function<void(int)> f2 = std::bind(&Foo::print_add, &foo, std::placeholders::_1);

4. 关键特性

类型擦除 (Type Erasure)

这是 std::function 的底层核心。它通过内部的一套模板机制,把不同类型的可调用对象"包装"起来,对外呈现统一的接口。这让你可以把不同的 Lambda 或函数存入同一个容器:

C++

复制代码
std::vector<std::function<void()>> tasks;
tasks.push_back([](){ /* 任务A */ });
tasks.push_back(std::bind(some_function));

空检查

std::function 可以处于"空"状态(即不指向任何目标)。调用空的 std::function 会抛出 std::bad_function_call 异常。

C++

复制代码
std::function<void()> f;
if (!f) { // 或者 if (f == nullptr)
    // 尚未赋值,直接调用会崩溃
}

小对象优化 (SBO)

为了性能,大多数 std::function 的实现包含一个小缓冲区。如果存储的可调用对象足够小(如没有捕获变量的 Lambda),它会直接存在栈上;如果对象太大,则会触发动态内存分配(堆分配)。


5. 性能权衡

虽然 std::function 很方便,但它并非没有代价:

特性 影响
虚函数调用 内部通常涉及间接调用(类似虚函数),无法像直接调用 Lambda 那样被编译器内联。
内存分配 捕获大量变量的 Lambda 可能导致堆内存分配。
类型安全 提供了运行时的灵活性,但在某些性能敏感场景下(如高频循环),直接使用模板函数会更快。

6. 与 std::bind 的关系

在 C++11 时代,两者经常配合使用。但随着 C++14/17 对 Lambda 的增强(如泛型 Lambda),std::bind 的使用场景正在萎缩。

建议: 除非需要处理非常复杂的参数重排,否则优先使用 Lambda 表达式来初始化 std::function,这样代码更易读,性能通常也更好。


7. 常见应用场景

  1. 回调系统:定义 UI 组件的点击事件处理。

  2. 任务队列:线程池中存储异步执行的任务。

  3. 策略模式:在运行时动态更换算法逻辑。

相关推荐
白杨尚青9 分钟前
C++进阶篇(二):多态——虚函数、重写、虚函数表与三个经典坑(附完整代码仓库)
开发语言·c++·多态·虚函数·重写·动态绑定
syagain_zsx19 分钟前
spdlog实战:日志分级、滚动与性能调优
linux·c++·日志库·二次开发日志库
weixin_3077791318 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
AI wins18 小时前
c++新增頭文件
c++
Shadow(⊙o⊙)19 小时前
仿muduo实现高并发服务器——事件就绪管理Channel.hpp
服务器·c++
by2099919 小时前
哈希表——快速找到数据:冲突、删除与扩容的实现
数据结构·c++·哈希算法·散列表
音草20 小时前
C++ 继承详解:从概念到菱形继承
c++
weixin_3077791320 小时前
C++代码实现MATLAB中的spectralcluster函数功能
开发语言·c++·算法·matlab
xiaoye-duck20 小时前
《C++ 项目:手写大模型接入 SDK》Trae IDE 开发环境搭建与依赖管理
c++·ide·语言模型
沫璃染墨21 小时前
《Qt从零入门系列(十四):QThread多线程详解——从线程创建到客户端阻塞处理》
开发语言·c++·笔记·qt·ui·交互·线程