类型擦除通过基类虚函数统一接口,模板派生类保存具体可调用对象,包装器持基类指针实现值语义,支持任意可调用类型但隐藏具体类型,如简易std::function实现所示。

类型擦除(Type Erasure)是一种让不同类型共享统一接口的技术,常用于实现类似 std::function 的泛型函数包装器。它能隐藏具体类型信息,只暴露行为接口,从而实现运行时多态而无需继承体系。
基本思路:封装可调用对象
目标是实现一个通用的函数包装器,支持任意可调用类型(函数指针、lambda、仿函数等),但对外呈现统一类型。核心是把“具体类型”擦除,只保留“调用能力”。
我们通过以下组件实现:
- 基类接口:定义调用操作的虚函数
- 模板派生类:保存具体可调用对象
- 包装器类:持有基类指针,提供值语义
手动实现简易 std::function
下面是一个简化版的函数包装器,支持无参返回 int 的调用:
立即学习“C++免费学习笔记(深入)”;
#include#include class Function { struct CallableBase { virtual ~CallableBase() = default; virtual int call() = 0; virtual std::unique_ptr clone() const = 0; }; template struct CallableModel : CallableBase { F func; explicit CallableModel(F f) : func(std::move(f)) {} int call() override { return func(); } std::unique_ptr clone() const override { return std::make_unique (func); } }; std::unique_ptr callable; public: Function() = default; template Function(F f) : callable(std::make_unique >(std::move(f))) {} Function(const Function& other) : callable(other.callable ? other.callable->clone() : nullptr) {} Function& operator=(const Function& other) { if (this != &other) { callable = other.callable ? other.callable->clone() : nullptr; } return *this; } Function(Function&&) = default; Function& operator=(Function&&) = default; int operator()() const { if (!callable) throw std::bad_function_call(); return callable->call(); } explicit operator bool() const { return !!callable; } };
使用示例:
int foo() { return 42; }
int main() {
Function f1 = []{ return 100; };
Function f2 = foo;
std::cout << f1() << "\n"; // 输出 100
std::cout << f2() << "\n"; // 输出 42
Function f3 = f1;
std::cout << f3() << "\n"; // 输出 100
}
支持任意签名和参数
要支持不同函数签名,可将 Function 改为模板:
templateclass Function; template class Function { struct CallableBase { virtual ~CallableBase() = default; virtual R call(Args... args) = 0; virtual std::unique_ptr clone() const = 0; }; template struct CallableModel : CallableBase { F func; explicit CallableModel(F f) : func(std::move(f)) {} R call(Args... args) override { return func(std::forward (args)...); } std::unique_ptr clone() const override { return std::make_unique (func); } }; std::unique_ptr callable; public: Function() = default; template Function(F f) : callable(std::make_unique >(std::move(f))) {} Function(const Function& other) : callable(other.callable ? other.callable->clone() : nullptr) {} Function& operator=(const Function& other) { if (this != &other) { callable = other.callable ? other.callable->clone() : nullptr; } return *this; } Function(Function&&) = default; Function& operator=(Function&&) = default; R operator()(Args... args) const { if (!callable) throw std::bad_function_call(); return callable->call(std::forward (args)...); } explicit operator bool() const { return !!callable; } };
性能与优化考虑
上述实现每次调用都有虚函数开销。实际如 std::function 会结合小对象优化(Small Buffer Optimization),在栈上存储小型可调用对象,避免堆分配。
关键点:
- 类型擦除牺牲部分性能换取接口统一
- 避免频繁拷贝包装器,尤其含大闭包的 lambda
- 注意异常安全和资源管理(使用智能指针)
基本上就这些。不复杂但容易忽略细节,比如正确实现拷贝语义和所有权转移。











