我正在阅读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_;
};
但我想知道是否有必要(或者换句话说,一个好的设计模式)来包装一个函数。因为一个Funciton封装成
FunctionWrapper
这么多层,比如targer function->ImplType->ImplBase->std::unique_ptr<ImplBase>->FunctionWrapper
,然后用它到thread_pool
就像一样
class ThreadPool {
...
private:
ConcurrentQueue<FunctionWrapper> q_;
};
简单来说,为什么只在 FunctionWrapper 中使用
std::unique_ptr<F> impl_
?
为什么只在 FunctionWrapper 中使用 std::unique_ptr impl_ ?
因为这不会是类型擦除。
这个类的重点是提供一个与函数的底层类型无关的相同类型。
我对这本书不熟悉,因此我使用标准类型擦除
std::function
作为示例:
// some function
void foo();
std::function<void()> f;
f = &foo;
f = [](){};
f
可以存储各种callable。存储 lambda 的 std::function<void()>
和存储指向自由函数的指针 std::function<void()>
具有相同类型:std::function<void()>
。只有具有不同签名的可调用对象才需要您使用不同的类型。例如,返回 int
的可调用对象可以存储在 std::function<int()>
中。
间接层并不算“太多”。这是类型擦除所需要的。粗略地说,从
ImplBase
的继承将不同类型的可调用对象之间的差异(编译时的差异)推向了虚拟调度(仅在运行时发生)。
当您对某些特定类型的可调用对象感到满意时,例如
void(*)()
,指向自由函数的指针,那么您不需要类型擦除。但是,您不能在函数指针中存储其他类型的可调用对象。例如,捕获变量的 lambda 无法转换为指向自由函数的指针。