std::function 是 C++ 标准库中的一个通用多态函数包装器。它可以存储、复制和调用任意可调用目标(函数、lambda 表达式、绑定表达式或其他函数对象)。 std::function 占有固定尺寸的内存,这是因为它的实现方式决定了这一点。让我们深入探讨这一点。
std::function 通常使用类型擦除和小对象优化(Small Object Optimization, SOO)来实现这一点。
由于 std::function 使用了类型擦除和小对象优化,其内部实现通常包含以下几个部分:
这意味着,无论存储的对象是多大或多小,std::function 的实例总是占用固定大小的内存,以包含这些指针和元数据。
假设我们有以下 std::function 声明:
std::function<void()> func;
其内部可能包含如下内容:
指向可调用对象的指针 或 内部缓冲区:
虚表指针:
元数据:
无论我们存储的是一个普通函数指针,一个小型 lambda 表达式,还是一个大型函数对象,std::function 实例的大小都是固定的。
#include
#include
void exampleFunction() {
std::cout << "Hello from function!" << std::endl;
}
int main() {
// 存储普通函数指针
std::function<void()> func1 = exampleFunction;
// 存储lambda表达式
std::function<void()> func2 = []() {
std::cout << "Hello from lambda!" << std::endl;
};
// 存储大对象
struct LargeFunctor {
void operator()() const {
std::cout << "Hello from large functor!" << std::endl;
}
int data[100];
};
std::function<void()> func3 = LargeFunctor();
// 调用
func1();
func2();
func3();
std::cout << "Size of func1: " << sizeof(func1) << std::endl;
std::cout << "Size of func2: " << sizeof(func2) << std::endl;
std::cout << "Size of func3: " << sizeof(func3) << std::endl;
return 0;
}
结果如下:
Hello from function!
Hello from lambda!
Hello from large functor!
Size of func1: 32
Size of func2: 32
Size of func3: 32
我们可以很明显得看出三个 std::function 对象大小都是一样的。
小总结:
- std::function 的大小是固定的,因为它使用类型擦除和小对象优化来处理不同类型的可调用对象。
- 这种固定大小的内存布局使得 std::function 可以有效地存储和管理多种不同类型的可调用对象,同时提供统一的接口来调用这些对象。
- 无论存储的对象是函数指针、小型 lambda 表达式,还是大型函数对象,std::function 的实例大小都是固定的。
运行时多态(dynamic polymorphism)指的是在程序运行时决定调用哪个具体的函数实现。通常通过虚函数和继承来实现。std::function 通过类型擦除和虚表机制提供了类似的功能,使得它可以在运行时处理不同类型的可调用对象。
#include
#include
// 一个接受 std::function 类型参数的函数
void invokeWithFive(const std::function<void(int)>& func) {
func(5); // 调用传入的可调用对象,并传递参数5
}
int main() {
// 使用普通函数指针
void (*funcPtr)(int) = [](int x) { std::cout << "Function pointer: " << x << std::endl; };
invokeWithFive(funcPtr);
// 使用 lambda 表达式
auto lambda = [](int x) { std::cout << "Lambda: " << x << std::endl; };
invokeWithFive(lambda);
// 使用函数对象
struct Functor {
void operator()(int x) const {
std::cout << "Functor: " << x << std::endl;
}
};
Functor functor;
invokeWithFive(functor);
return 0;
}
在运行时,当我们调用 invokeWithFive(funcPtr)、invokeWithFive(lambda) 和 invokeWithFive(functor) 时,std::function 会根据存储的不同可调用对象类型,调用相应的实现。这种行为是运行时决定的,因此属于运行时多态。