C++中的函数包装器(Function Wrapper)是用来封装和管理函数或可调用对象(如函数指针、函数对象、Lambda 表达式等)的工具。它们使得函数的使用更为灵活和通用,常被用于异步编程、事件处理、回调等场景。
C++11引入的 std::function 是最常用的函数包装器。它可以存储任何可调用对象并提供统一的调用接口。以下是关于函数包装器的详细讲解,包括它的基本用法、特点、限制、以及与其他相关机制的对比。
一、std::function 的基本用法
std::function 是 C++ 标准库中的一个模板类,可以存储一个可调用对象,如普通函数、函数指针、Lambda 表达式、或实现了 operator() 的对象。
1. 基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
#include <functional>
void myFunction(int x) {
std::cout << "Function called with: " << x << std::endl;
}
int main() {
std::function<void(int)> func = myFunction; // 包装普通函数
func(10); // 输出: Function called with: 10
// 也可以包装 Lambda 表达式
func = [](int y) {
std::cout << "Lambda called with: " << y << std::endl;
};
func(20); // 输出: Lambda called with: 20
return 0;
}
|
二、如何使用 std::function
1. 存储不同类型的可调用对象
std::function 可以存储任何可以调用的对象,包括函数、Lambda 表达式和函数对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <iostream>
#include <functional>
int add(int a, int b) {
return a + b;
}
struct Multiply {
int operator()(int a, int b) const {
return a * b;
}
};
int main() {
std::function<int(int, int)> func;
// 包装普通函数
func = add;
std::cout << "Add: " << func(3, 4) << std::endl; // 输出: Add: 7
// 包装函数对象
func = Multiply();
std::cout << "Multiply: " << func(3, 4) << std::endl; // 输出: Multiply: 12
// 包装 Lambda 表达式
func = [](int a, int b) { return a - b; };
std::cout << "Subtract: " << func(10, 4) << std::endl; // 输出: Subtract: 6
return 0;
}
|
2. 使用类型推导
C++14引入了泛型Lambda,进一步增强了 std::function 的灵活性:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
#include <functional>
int main() {
// 使用 Lambda 表达式的类型推导
auto lambda = [](int x) { return x * 2; };
std::function<int(int)> func = lambda;
std::cout << "Lambda result: " << func(5) << std::endl; // 输出: Lambda result: 10
return 0;
}
|
三、特点与限制
1. 类型安全
std::function 提供强类型安全,确保传递的可调用对象与指定的函数签名相符。
2. 存储开销
std::function 是一个类型擦除(Type Erasure)机制的实现,它会根据保存的可调用对象的类型动态分配内存。尽管这使得类型更灵活,但也增加了一些运行时开销。
3. 性能考虑
由于类型擦除的特性,std::function 的性能通常低于直接使用函数指针或 Lambda 表达式,特别是在高频调用的场景下。如果对性能有较高要求,建议直接使用函数指针或模板。
四、结合 std::bind
std::bind 是一个C++11引入的函数适配器,允许将某些参数绑定到函数对象或 Lambda 表达式。与 std::function 结合使用可以使代码更灵活。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iostream>
#include <functional>
void print(int x, int y) {
std::cout << "x: " << x << ", y: " << y << std::endl;
}
int main() {
// 使用 std::bind 绑定部分参数
auto boundFunc = std::bind(print, 10, std::placeholders::_1);
// `boundFunc` 现在只需要一个参数
boundFunc(20); // 输出: x: 10, y: 20
return 0;
}
|
五、结合标准库的异步操作
在使用异步处理时,std::function 可以存储要在新线程中执行的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <iostream>
#include <functional>
#include <thread>
void asyncTask(int id) {
std::cout << "Task " << id << " is running." << std::endl;
}
int main() {
// 声明 std::function
std::function<void(int)> task = asyncTask;
// 创建新线程
std::thread t(task, 1);
t.join(); // 等待线程结束
return 0;
}
|
|