我确定我要寻找的名称是一个名字,我只是不知道(如果知道的话,我可能已经找到答案了)。基本上,我想为体育运动实现自己的std::function
轻量级版本。我想用lambda初始化它,然后再调用它。我可以用模板包装器类包装lambda,没什么用:
struct CInvokableAbstract {
virtual ~CInvokableAbstract() = default;
};
template <class InvokableObject>
struct CInvokableBasic : public CInvokableAbstract
{
CInvokableBasic(InvokableObject&& target) : _invokable(std::move(target)) {}
template <typename... Args>
typename std::result_of<decltype(&InvokableObject::operator())(Args...)>::type operator()(Args... args) const
{
return _invokable(std::forward<Args>(args)...);
}
private:
InvokableObject _invokable;
};
现在,我可以使我的类在语义上类似于std::function
,但是如何存储lambda的确切类型,以便将经过类型擦除的对象转换回其原始的具体类型?
struct CInvokableDeferred
{
template <class InvokableObject>
CInvokableDeferred(InvokableObject&& target) noexcept : _invokable(std::make_unique<CInvokableBasic<InvokableObject>>(std::move(target))) {}
template <typename... Args>
void operator()(Args... args) const
{
// How can I know the original concrete type to cast this to?
static_cast<???>(_invokable.get())->(std::forward<Args>(args)...);
}
private:
std::unique_ptr<CInvokableAbstract> _invokable;
};
我想不出任何可以做到这一点的模板技巧,但我们知道这是可能的(除非std::function
使用某些内置的编译器,否则将在编译器内部实现,而不是作为常规的C ++代码)。
请注意,我使用的编译器没有完整的C ++ 17支持,因此不能使用e。 G。 auto
得出的返回类型。
答案 0 :(得分:4)
您需要按如下方式重写您的基类:
template <typename Ret, typename... Args>
class CInvokableAbstract {
virtual ~CInvokableAbstract() = default;
virtual Ret operator()(Args... args) = 0;
};
这将使您的基类依赖于签名(必须是签名才能使用),并提供可调用对象的实际接口。
请注意,这部分代码实际上与类型擦除无关,只是普通的老式动态多态性。静态(CInvokableBasic
模板)和动态(CInvokableAbstract
接口)多态性的组合使类型擦除成为可能。