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

相关推荐
2601_962218611 小时前
C++中decltype关键字的实现
开发语言·c++
繁华的地方不一定留下你的脚印2 小时前
C++ std::variant 与 std::visit:安全保存多种类型,写清每个处理分支
开发语言·c++
程序员老陆2 小时前
WIN32_LEAN_AND_MEAN:Windows 头文件里的“精简编译开关”
c++·windows
hold?fish:palm3 小时前
45 二叉树的右视图
开发语言·c++·算法
ShineWinsu3 小时前
对于Redis:Redis的认识以及分布式系统的解析
linux·数据库·c++·redis·缓存·消息队列·分布式架构
布莱克6053 小时前
在C++中,构造函数能不能是虚函数?析构函数为什么要虚?
开发语言·c++·构造函数·析构函数·虚函数
旖旎夜光4 小时前
LeetCode 137 : 只出现一次的数字 II(位运算) —— 题解
c++·笔记·学习·算法·leetcode·力控
王老师青少年编程4 小时前
2026年9月GESP真题及题解(C++七级):必经之路
c++·题解·真题·gesp·七级·2026年9月·必经之路
码匠许师傅5 小时前
【C++三方组件】Asio 上篇:回调式 TCP 客户端与服务端
开发语言·c++·tcp/ip
沐欣工作室_lvyiyi5 小时前
家庭水资源节能系统设计(论文+源码)
c++·python·云平台·单片机毕业设计·单片机仿真·电子信息工程毕业设计·机械电子工程