std::function 的源码实现涉及类型擦除、多态封装和资源管理等核心技术,不同标准库(如 libstdc++、libc++)的实现细节略有差异,但核心原理一致。下面通过一个简化的模拟实现,展示其关键组件和工作机制(忽略标准库中的优化细节,如小对象优化 SSO 等)。
核心组件拆解
std::function 的实现主要依赖三个部分:
- 抽象基类:定义统一的调用接口(虚函数)。
- 派生类模板:继承基类,存储具体可调用对象,并实现基类接口。
function类:持有基类指针,管理派生类对象的生命周期,提供统一的调用入口。
简化实现代码
以下是模拟 std::function 的核心代码,支持存储、复制、移动和调用可调用对象:
#include <utility> // 用于std::forward
#include <algorithm> // 用于std::swap
#include <stdexcept> // 用于std::bad_function_call
// 前向声明
template <typename Signature>
class my_function;
// 特化版本:处理函数签名(返回值Ret,参数Args...)
template <typename Ret, typename... Args>
class my_function<Ret(Args...)> {
private:
// 抽象基类:定义统一接口
struct function_base {
virtual ~function_base() = default;
// 调用接口:纯虚函数,由派生类实现
virtual Ret operator()(Args... args) const = 0;
// 克隆自身:用于复制构造
virtual function_base* clone() const = 0;
};
// 派生类模板:存储具体可调用对象
template <typename F>
struct function_impl : function_base {
F func; // 存储可调用对象(函数、lambda、仿函数等)
// 构造函数:接收可调用对象并存储
explicit function_impl(F&& f) : func(std::forward<F>(f)) {}
// 实现调用:转发参数给存储的可调用对象
Ret operator()(Args... args) const override {
return func(std::forward<Args>(args)...); // 完美转发
}
// 克隆自身:创建当前对象的副本(用于my_function的复制)
function_base* clone() const override {
return new function_impl<F>(func); // 复制存储的可调用对象
}
};
function_base* impl = nullptr; // 基类指针,指向派生类对象
public:
// 默认构造:空状态
my_function() noexcept = default;
// 模板构造:接收任意可调用对象(F需能匹配Ret(Args...)签名)
template <typename F>
my_function(F&& f) {
// 构造派生类对象,存储f的副本
impl = new function_impl<F>(std::forward<F>(f));
}
// 复制构造:通过clone()复制内部实现
my_function(const my_function& other) {
if (other.impl) {
impl = other.impl->clone(); // 深拷贝
}
}
// 移动构造:转移资源所有权
my_function(my_function&& other) noexcept : impl(other.impl) {
other.impl = nullptr; // 源对象置空
}
// 复制赋值
my_function& operator=(const my_function& other) {
if (this != &other) {
my_function temp(other); // 利用复制构造创建临时对象
swap(temp); // 交换资源(异常安全)
}
return *this;
}
// 移动赋值
my_function& operator=(my_function&& other) noexcept {
if (this != &other) {
delete impl; // 释放当前资源
impl = other.impl; // 接管源对象资源
other.impl = nullptr; // 源对象置空
}
return *this;
}
// 析构:释放内部资源
~my_function() {
delete impl;
}
// 交换两个my_function的资源
void swap(my_function& other) noexcept {
std::swap(impl, other.impl);
}
// 调用操作符:转发给内部实现
Ret operator()(Args... args) const {
if (!impl) {
throw std::bad_function_call(); // 空状态调用抛出异常
}
return (*impl)(std::forward<Args>(args)...); // 多态调用
}
// 检查是否为空(是否存储了可调用对象)
explicit operator bool() const noexcept {
return impl != nullptr;
}
};
代码解析
1. 抽象基类 function_base
- 定义了
std::function所需的统一接口,包括:- 虚析构函数:确保派生类对象能被正确销毁。
- 纯虚函数
operator():接收Args...类型参数,返回Ret类型,是调用可调用对象的入口。 - 纯虚函数
clone():用于复制自身(实现my_function的复制构造)。
2. 派生类模板 function_impl<F>
- 模板参数
F是具体可调用对象的类型(如函数、lambda、仿函数等)。 - 成员变量
func存储可调用对象的副本(通过完美转发构造,减少复制开销)。 - 实现
function_base的接口:operator():调用func并转发参数(完美转发确保参数传递的正确性)。clone():创建function_impl<F>的新实例,复制func(实现深拷贝)。
3. my_function 类(模拟 std::function)
- 核心成员
impl是function_base*类型,通过多态指向function_impl<F>对象(类型擦除的关键)。 - 构造函数:接收任意可调用对象
F,构造function_impl<F>并让impl指向它。 - 复制/移动语义:
- 复制:通过
clone()深拷贝impl指向的对象。 - 移动:直接转移
impl的所有权(源对象置空)。
- 复制:通过
- 调用操作:通过
impl->operator()触发多态调用,实际执行function_impl<F>中存储的可调用对象。 - 空状态检查:通过
impl != nullptr判断是否存储了可调用对象,空状态调用抛出bad_function_call。
使用示例
#include <iostream>
#include "my_function.h"
// 普通函数
int add(int a, int b) { return a + b; }
// 仿函数
struct Multiply {
int operator()(int a, int b) const { return a * b; }
};
int main() {
// 存储普通函数
my_function<int(int, int)> func1 = add;
std::cout << "add: " << func1(2, 3) << std::endl; // 输出5
// 存储lambda表达式
my_function<int(int, int)> func2 = [](int a, int b) { return a - b; };
std::cout << "subtract: " << func2(5, 2) << std::endl; // 输出3
// 存储仿函数
my_function<int(int, int)> func3 = Multiply{};
std::cout << "multiply: " << func3(2, 3) << std::endl; // 输出6
// 复制和移动
my_function<int(int, int)> func4 = func3; // 复制
std::cout << "func4: " << func4(3, 4) << std::endl; // 输出12
my_function<int(int, int)> func5 = std::move(func4); // 移动
std::cout << "func5: " << func5(3, 4) << std::endl; // 输出12
std::cout << "func4 is " << (func4 ? "not empty" : "empty") << std::endl; // 输出empty
return 0;
}
实际标准库的优化
上述简化实现仅展示核心原理,实际标准库(如 libstdc++)还有以下优化:
-
小对象优化(SSO):
对于小型可调用对象(如无捕获的 lambda、函数指针),直接存储在std::function内部的栈上缓冲区,避免动态内存分配(new/delete),提升性能。 -
异常安全增强:
实际实现中会更严格地处理异常(如clone()过程中抛出异常时的资源释放)。 -
支持更多可调用类型:
对成员函数指针、std::bind返回的绑定器等特殊可调用对象进行适配。 -
空状态优化:
可能用空指针或特殊标记表示空状态,减少内存开销。
总结
std::function 的核心实现依赖**“基类接口 + 派生类模板”的多态结构**,通过类型擦除统一封装各种可调用对象。其关键是用基类指针存储具体可调用对象的派生类实例,通过虚函数调用实现多态,对外提供统一的调用接口。
这种设计平衡了灵活性(支持任意可调用对象)和易用性(统一的类型和接口),使其成为 C++ 中处理回调、策略模式等场景的核心工具。

被折叠的 条评论
为什么被折叠?



