Как сохранить и восстановить точный тип стертого объекта? - PullRequest
0 голосов
/ 15 сентября 2018

Я уверен, что есть название для того, что я ищу, я просто не знаю его (и если бы я знал, я бы, наверное, уже нашел ответ).По сути, я хочу реализовать свою собственную облегченную версию std::function для спорта.Я хочу инициализировать его с помощью лямбды, а затем вызвать его.Я могу обернуть лямбду своим классом-оберткой для шаблона, ничего для него:

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, но как я могу сохранить точный тип лямбды вЧтобы преобразовать стертый объект обратно в его исходный конкретный тип?

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, поэтому я могуНе используйте, например, auto -возвратный тип возврата.

1 Ответ

0 голосов
/ 16 сентября 2018

Вам нужно переписать свой базовый класс следующим образом:

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

Это сделает ваш базовый класс зависимым от подписи (которым он должен быть для того, чтобы его можно было использовать) и обеспечит фактический интерфейс для вызываемого объекта.

Обратите внимание, что эта часть кода на самом деле не имеет ничего общего со стиранием типов, это просто старый динамический полиморфизм. Это сочетание статического (CInvokableBasic шаблон) и динамического (CInvokableAbstract интерфейс) полиморфизмов, которые делают возможным стирание типа.

...