如何存储和恢复类型擦除对象的确切类型?

时间:2018-09-15 20:42:59

标签: c++ c++11 c++14 type-erasure

我确定我要寻找的名称是一个名字,我只是不知道(如果知道的话,我可能已经找到答案了)。基本上,我想为体育运动实现自己的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得出的返回类型。

1 个答案:

答案 0 :(得分:4)

您需要按如下方式重写您的基类:

template <typename Ret, typename... Args>
class CInvokableAbstract {
   virtual ~CInvokableAbstract() = default;
   virtual Ret operator()(Args... args) = 0;
};

这将使您的基类依赖于签名(必须是签名才能使用),并提供可调用对象的实际接口。

请注意,这部分代码实际上与类型擦除无关,只是普通的老式动态多态性。静态(CInvokableBasic模板)和动态(CInvokableAbstract接口)多态性的组合使类型擦除成为可能。