答案是使用虚函数实现类型擦除的核心在于通过抽象基类定义统一接口,模板派生类封装具体类型,外部类持有基类指针以实现多态调用。示例中AnyFunction通过继承体系包装任意可调用对象,调用时无需知晓原始类型,从而实现类型无关的接口统一。

在C++中,类型擦除(Type Erasure)是一种让不同类型的对象表现出统一接口的技术,同时隐藏其具体类型。它常用于实现像 std::function、std::any 这样的通用容器。核心思想是将具体类型“擦除”,通过统一的接口调用底层操作。
使用虚函数实现类型擦除(基于多态)
最常见的方法是借助基类的虚函数机制,把具体类型封装到派生类中,对外暴露统一接口。
步骤:
- 定义一个抽象基类,提供需要的公共接口(如调用、复制等)。
- 模板派生类继承该基类,封装具体类型和操作。
- 外部类持有基类指针,实现类型无关的操作。
示例:实现一个简易的任意可调用对象包装器
立即学习“C++免费学习笔记(深入)”;
#include#include #include // 抽象基类 struct FunctionBase { virtual void call() const = 0; virtual std::unique_ptr clone() const = 0; virtual ~FunctionBase() = default; }; // 模板派生类 template struct FunctionWrapper : FunctionBase { F f; FunctionWrapper(F f) : f(std::move(f)) {} void call() const override { f(); } std::unique_ptr clone() const override { return std::make_unique (f); } }; // 外部接口类,用户使用 class AnyFunction { std::unique_ptr func; public: template AnyFunction(F f) : func(std::make_unique >(std::move(f))) {} AnyFunction(const AnyFunction& other) : func(other.func->clone()) {} AnyFunction& operator=(const AnyFunction& other) { func = other.func->clone(); return *this; } void operator()() const { func->call(); } };
使用方式:
```cpp void hello() { std::cout 基于模板和函数指针的轻量级类型擦除避免虚函数开销,可以用函数指针+void* 来存储数据和操作函数。
原理: 将操作函数和数据指针绑定,运行时通过函数指针调用。
class SimpleFunction {
void* data = nullptr;
void (*call_func)(void*) = nullptr;
void (*copy_func)(void*, const void*) = nullptr;
public:
template
SimpleFunction(F f) {
struct Storage {
F func;
static void call(void* p) {
static_cast(p)->func();
}
static void copy(void* dst, const void* src) {
new(dst) Storage(*static_cast(src));
}
};
// 假设栈上分配或小对象内嵌(简化起见用new)
data = new Storage{std::move(f)};
call_func = &Storage::call;
copy_func = &Storage::copy;
}
void operator()() const {
call_func(data);
}
~SimpleFunction() {
// 实际需记录大小并调用析构,此处略
}
}; 这种方式更接近 std::function 的内部实现,支持小对象优化(SOO)时性能更好。
常见应用场景
- std::function:包装任意可调用对象。
- std::any / boost::any:存储任意类型值。
- 信号槽系统:回调函数统一管理。
- 插件/模块接口:隐藏实现细节。
基本上就这些。类型擦除的关键是分离接口与实现,通过中间层屏蔽具体类型。用虚函数最直观,用函数指针+void* 更高效但实现复杂些。根据需求选择合适方式即可。











