类封装层数过多是否更优?FunctionWrapper多层封装必要性探讨
关于C++类型擦除函数包装器的设计疑问解答
我正在阅读《CPP-Concurrency-In-Action-2ed-2019》一书,在第9.1.2章中看到作者给出的类型擦除函数包装器实现:
#include <memory> #include <utility> class FunctionWrapper { public: FunctionWrapper() = default; FunctionWrapper(const FunctionWrapper&) = delete; FunctionWrapper& operator=(const FunctionWrapper&) = delete; FunctionWrapper(FunctionWrapper&& rhs) noexcept : impl_(std::move(rhs.impl_)) {} FunctionWrapper& operator=(FunctionWrapper&& rhs) noexcept { impl_ = std::move(rhs.impl_); return *this; } template <typename F> FunctionWrapper(F&& f) : impl_(new ImplType<F>(std::move(f))) {} void operator()() const { impl_->call(); } private: struct ImplBase { virtual void call() = 0; virtual ~ImplBase() = default; }; template <typename F> struct ImplType : ImplBase { ImplType(F&& f) noexcept : f_(std::move(f)) {} void call() override { f_(); } F f_; }; private: std::unique_ptr<ImplBase> impl_; };
对此我有以下疑问:
- 对函数进行多层封装(目标函数→ImplType→ImplBase→std::unique_ptr
→FunctionWrapper)是否必要? - 这是否属于良好的设计模式?
- 在线程池ThreadPool中使用该包装器时,为何不直接在FunctionWrapper中使用std::unique_ptr
作为成员?
class ThreadPool { ... private: ConcurrentQueue<FunctionWrapper> q_; };
解答:
1. 多层封装的必要性:核心是实现类型擦除
这个包装器的核心目标是让FunctionWrapper成为一个类型统一的容器,可以容纳任意可调用对象(lambda、函数指针、自定义 functor 等)。如果直接用std::unique_ptr<F>作为成员,FunctionWrapper会变成模板类——每个不同的可调用类型F都会生成不同的FunctionWrapper实例,这会导致线程池的任务队列ConcurrentQueue<FunctionWrapper>失去意义:队列只能存储同一种F类型的任务,根本无法混合不同逻辑的任务,完全不符合线程池的设计需求。
而多层封装正是实现类型擦除的关键:
ImplBase作为抽象基类,定义了统一的调用接口call(),把具体可调用类型的细节擦除,只保留统一的操作入口。ImplType是模板子类,针对具体的可调用类型F,实现call()方法来调用F的operator(),完成统一接口到具体类型的绑定。std::unique_ptr<ImplBase>负责管理底层实例的生命周期,同时借助多态特性,通过基类指针调用子类的call()方法,无需关心底层具体是哪种可调用类型。
2. 这是一种优秀的设计模式
这是典型的类型擦除设计模式,和标准库中std::function的实现思路完全一致。它完美解决了“需要用统一类型存储和操作不同可调用对象”的场景,在并发编程(比如线程池任务队列)中尤为实用——线程池需要接收任意逻辑的任务,同时要求所有任务的类型一致才能存入队列,类型擦除正好满足这个需求。
3. 为何不能直接用std::unique_ptr<F>?
除了前面提到的“无法统一类型存储不同任务”的问题外,还有这些弊端:
- 模板类的
FunctionWrapper会导致代码膨胀,每个不同的F都会生成一份独立的类代码。 - 无法实现抽象性:模板类的
FunctionWrapper不能作为非模板函数的参数传递,也不能存入非模板容器,失去了作为通用任务包装器的灵活性。
内容的提问来源于stack exchange,提问作者f1msch
相关产品推荐
相关产品推荐

