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

类型擦除(Type Erasure)是一种让不同类型共享统一接口的技术,常用于实现类似 std::function 的泛型函数包装器。它能隐藏具体类型信息,只暴露行为接口,从而实现运行时多态而无需继承体系。
目标是实现一个通用的函数包装器,支持任意可调用类型(函数指针、lambda、仿函数等),但对外呈现统一类型。核心是把“具体类型”擦除,只保留“调用能力”。
我们通过以下组件实现:
下面是一个简化版的函数包装器,支持无参返回 int 的调用:
立即学习“C++免费学习笔记(深入)”;
#include <memory>
#include <iostream>
class Function {
struct CallableBase {
virtual ~CallableBase() = default;
virtual int call() = 0;
virtual std::unique_ptr<CallableBase> clone() const = 0;
};
template <typename F>
struct CallableModel : CallableBase {
F func;
explicit CallableModel(F f) : func(std::move(f)) {}
int call() override { return func(); }
std::unique_ptr<CallableBase> clone() const override {
return std::make_unique<CallableModel>(func);
}
};
std::unique_ptr<CallableBase> callable;
public:
Function() = default;
template <typename F>
Function(F f) : callable(std::make_unique<CallableModel<F>>(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 改为模板:
template <typename Signature>
class Function;
template <typename R, typename... Args>
class Function<R(Args...)> {
struct CallableBase {
virtual ~CallableBase() = default;
virtual R call(Args... args) = 0;
virtual std::unique_ptr<CallableBase> clone() const = 0;
};
template <typename F>
struct CallableModel : CallableBase {
F func;
explicit CallableModel(F f) : func(std::move(f)) {}
R call(Args... args) override { return func(std::forward<Args>(args)...); }
std::unique_ptr<CallableBase> clone() const override {
return std::make_unique<CallableModel>(func);
}
};
std::unique_ptr<CallableBase> callable;
public:
Function() = default;
template <typename F>
Function(F f) : callable(std::make_unique<CallableModel<F>>(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>(args)...);
}
explicit operator bool() const { return !!callable; }
};
上述实现每次调用都有虚函数开销。实际如 std::function 会结合小对象优化(Small Buffer Optimization),在栈上存储小型可调用对象,避免堆分配。
关键点:
基本上就这些。不复杂但容易忽略细节,比如正确实现拷贝语义和所有权转移。
以上就是c++++怎么实现一个类型擦除(Type Erasure)的函数包装器_C++泛型编程与类型擦除技巧的详细内容,更多请关注php中文网其它相关文章!
c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号