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. 策略模式:在运行时动态更换算法逻辑。

相关推荐
初願致夕霞1 小时前
Linux编程_应用层_HTTP与HTTPS协议
linux·c++·http·https
水云桐程序员2 小时前
C++在游戏领域的项目案例有哪些?
jvm·c++·游戏
叼烟扛炮2 小时前
C++第五讲:内存管理
c++·算法·面试·内存管理
Ricky_Theseus2 小时前
vector 与 list 区别 + 使用场景
c++
代码中介商2 小时前
C++ 异常处理完全指南
开发语言·c++
Epiphany.5562 小时前
连通块的遍历
c++·算法·蓝桥杯
史迪仔01123 小时前
[QML] Qt6/Qt5四大渐变效果实战指南
开发语言·前端·c++·qt
张赫轩(不重名)3 小时前
加权重心(换根DP)
c++·算法·动态规划·图论
2401_840105203 小时前
题解: [GESP202409 八级] 美丽路径
数据结构·c++·算法·动态规划