C++11 std::function 用法详解:从基础到进阶实践
std::function 是 C++11 引入的一种通用多态函数包装器,能够存储、封装并调用各种可调用对象(如普通函数、Lambda 表达式、成员函数指针等)。它提供了类型擦除能力,使得在不同上下文中统一处理函数逻辑成为可能。
一、std::function 基础介绍
std::function 的核心作用是屏蔽底层可调用对象的差异,提供一个统一的接口进行调用。其基本定义需要指定返回值类型和参数类型:
template<typename RetType>
class RetType {
public:
// ...
};
std::function<Return_Type(Parameter_Types)> var_name;关键特性:
- 类型擦除:可以容纳任意满足签名要求的可调用类型。
- 空值检测:可以使用 bool 运算符检查对象是否为空(即是否绑定了有效的可调用对象)。
- 包含头文件:使用时需引用
#include <functional>。
第 1 步:基础声明与赋值
可以通过函数指针或类成员函数初始化 std::function:
#include <iostream>
#include <functional>
// 普通函数
int func(int x, int y) { return x + y; }
// 类结构
struct A {
int mem_func(int x) { return x * x; }
};
void example() {
// 绑定普通函数
std::function<int(int, int)> f = func;
// 绑定成员函数指针(注意:第一个参数通常隐含为 this 指针或实例引用)
std::function<int(A*, int)> f2 = &A::mem_func;
}第 2 步:状态检查与安全调用
在调用前判断对象是否为空是防止运行时错误的关键步骤:
std::function<int(int, int)> f_empty;
if (f_empty)
std::cout << f_empty(1, 2) << std::endl;
else
std::cout << "f is empty" << std::endl;二、进阶使用方法
第 3 步:结合 std::bind 使用
当需要固定部分参数或调整参数顺序时,std::bind 是常用搭档。以下示例展示了如何使用占位符 std::placeholders::_1 来构建灵活的绑定关系:
#include <iostream>
#include <functional>
using namespace std::placeholders;
void test1() { std::cout << "function called" << std::endl; }
int test2(int i) { return i * 10; }
int test3(int i, int j) { return i + j; }
struct A {
void foo(int i) { std::cout << i << std::endl; }
};
int main() {
// 1. 无参函数绑定
std::function<void()> fn1 = std::bind(test1);
// 2. 单参数映射
std::function<int(int)> fn2 = std::bind(test2, _1);
// 3. 双参数映射
std::function<int(int, int)> fn3 = std::bind(test3, _1, _2);
// 4. 绑定固定参数 (currying/偏应用)
std::function<int(int)> fn4 = std::bind(test3, 3, _1);
std::function<int()> fn5 = std::bind(test3, 3, 4);
// 5. 绑定对象成员函数
A a;
std::function<void(int)> fn6 = std::bind(&A::foo, &a, _1);
// 执行验证
fn1(); // 输出: function called
std::cout << fn2(1) << std::endl; // 输出: 10
std::cout << fn3(2, 3) << std::endl; // 输出: 5
std::cout << fn4(3) << std::endl; // 输出: 6
std::cout << fn5() << std::endl; // 输出: 7
fn6(8); // 输出: 8
return 0;
}第 4 步:存储 Lambda 表达式
Lambda 表达式是现代 C++ 中最常见的 std::function 来源。它可以捕获当前作用域的变量,实现更复杂的状态管理:
// 简单的 Lambda 加法
auto lambda_add = [](int a, int b) { return a + b; };
std::function<int(int, int)> add_fn = lambda_add;
std::cout << add_fn(10, 20) << std::endl; // 输出: 30第 5 步:结合智能指针管理资源
虽然 std::function 本身不负责内存所有权,但在某些高级场景下,可以将具有特定生命周期管理的对象包装入其中。需要注意的是,标准的 std::make_shared 并不直接用于创建 std::function 对象,而是用于创建被包装的对象。下面的示例演示了如何通过工厂模式或特定的初始化方式结合智能指针,但核心重点在于理解 std::function 的泛型存储能力:
// 典型用法:存储指向具体功能实现的共享指针
// 注意:这里的写法旨在展示如何存储一个通用的函数子对象类型
// 并非标准库直接提供 make_shared<std::function> 的构造方式,
// 更多时候我们直接使用 lambda 捕获智能指针来实现资源管理。
std::shared_ptr<int> resource = std::make_shared<int>(100);
std::function<int()> get_resource = [&]() { return *resource; };
std::cout << get_resource() << std::endl; // 输出: 100三、注意事项与限制
- 无法重载函数:
std::function只能针对具体的签名(返回类型+参数列表)创建实例。如果存在同名重载函数,必须明确指定类型或通过 Lambda 进行转发。 - 性能开销:由于涉及类型擦除和多态分发,
std::function的调用速度略低于直接的函数指针调用或编译器能内联优化的 Lambda。在高性能计算瓶颈中需权衡使用。 - 循环引用:在 Lambda 捕获时使用
this指针需谨慎,避免与类内部其他持有该std::function成员的逻辑产生循环引用导致内存泄漏。建议使用weak_ptr或确保作用域清晰。
如需查阅更详细的 API 文档和标准规范,推荐参考以下权威链接:
- C++11/14/17/20 Standard Reference on cppreference.com
- cplusplus.com functional reference
以上就是 C++11 中 std::function 基础用法详解的详细内容,更多关于 C++ std::function 的技巧资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













