【问题标题】:Deduction of Return / Argument types from function从函数中扣除返回/参数类型
【发布时间】:2021-02-19 17:50:03
【问题描述】:

我有一个回调对象的类型层次结构。经过大量的简化后,它看起来有点像这样:

template <typename Return, typename... Args>
struct Callback
{
  virtual Return Call(Args...) =0;

  void* mCtx;
}; // ctor, virtual dtor omitted

template <class T, typename Return, typename... Args>
struct MemberCallback: Callback<Return, Args...>
{
  Return Call(Args... args) override
  {
    return (static_cast<T*>(mCtx)->*mFn)(args...);
  }
  
  Return(T::*mFn)(Args...);
}; // ctor, specialisation for Return = void omitted

template <typename Return, typename... Args>
struct FunctionPtrCallback: Callback<Return, Args...>
{
  Return Call(Args... args) override
  {
    return mFn(args..., mCtx);
  }
  
  Return(*mFn)(Args..., void*);
}; // ctor, specialisation for Return = void omitted

还有一个工厂函数来推断MemberCallback&lt;&gt;s的类型:

template <class T, typename Return, typename... Args>
MemberCallback<T, Return, Args...> MakeCallback(T& object, Return(T::*fn)(Args...))
{
  return MemberCallback<T, Return, Args...>(&object, fn);
} // overload for const methods omitted

我正在尝试为FunctionPtrCallback&lt;&gt;s 设置类似的工厂函数,但是我一直遇到错误:

幼稚的方法FunctionPtrCallback&lt;Return, Args...&gt; MakeCallback(Return(*fn)(Args..., void*), void*) 在未能从void (ActualTypeUsed,void *) (C2784) 推断出Return (__cdecl *)(Args...,void *) 的模板参数后,会抱怨未能找到合适的重载 (C2672)。

编辑:明确指定模板参数 - MakeCallback&lt;void, ActualTypeUsed&gt; 将使其编译,除了只接受单个 void* (MakeCallback&lt;void&gt;()) 的函数。

(如果我在 MakeCallback 声明中从 fn 的签名中省略了 void* 参数,它会编译,但会实例化错误的 FunctionPtrCallback 模板 - FunctionPtrCallback&lt;void, ActualTypeUsed, void*&gt; 而不是 FunctionPtrCallback&lt;void, ActualTypeUsed&gt;。)

然后我尝试使用临时“功能特征”模板,类似于https://stackoverflow.com/a/9065203/79344 中建议的模板:

template <typename T>
struct FnTraits;

template <typename Return, typename... Args>
struct FnTraits<Return(Args...)>
{
  FunctionPtrCallback<Return, Args...> MakeCallback(Return(*fn)(Args..., void*), void* data)
  {
    return FunctionPtrCallback<Return, Args...>(fn, data);
  }
};

现在投诉是关于在我的FnTraits&lt;decltype(MyCallback)&gt;::MakeCallback(MyCallback, myData) 的嵌套名称说明符中使用不完整类型。将 MyCallback 的声明放在 FnTraits&lt;&gt; 之前(但不切实际)也无济于事。

1,用简洁的学术术语来说,为什么我的方法是错误的/我对模板推导有什么误解?

2,如果有办法让它工作,从我的回调中自动推断出ReturnArgs...,它是什么?

(我知道std::function&lt;&gt;;我宁愿避免使用它。)

编辑:godbolt link

【问题讨论】:

标签: c++ templates function-pointers type-deduction


【解决方案1】:

不幸的是,这似乎是一个包扩展,其中包不在列表末尾,例如Return(*)(Args..., void*),是一个非推导上下文(参见#8 here;免责声明:我不是语言律师)。这意味着在您的示例中,Args 无法从 FooBaz 的类型推导出来。

但是,您可以通过将参数包推断为 include void*,然后稍后删除 void* 来解决此问题:https://godbolt.org/z/aTTqbc

// Given Args that doesn't include void*, take the parameter pack
// out of the tuple and put it into the desired FunctionPtrCallback type.
template <typename Return, typename Tuple>
struct MakeCallbackImpl;
template <typename Return, typename... Args>
struct MakeCallbackImpl<Return, std::tuple<Args...>> {
    using type = FunctionPtrCallback<Return, Args...>;
};

// Given Args that *does* include void*, remove it using RemoveLast.
// Args can now be deduced because it appears last in Return(*)(Args...).
template <typename Return, typename... Args>
auto MakeCallback(Return(*fn)(Args...), void* data) {
    return typename MakeCallbackImpl<Return, typename RemoveLast<Args...>::type>::type(fn, data);
}

还有受this forum thread 启发的助手RemoveLast(但使用std::tuple_cat 是为了方便而不是定义自定义连接):

template<typename First, typename... Rest>
struct RemoveLast {
    using type = decltype(std::tuple_cat(
        std::declval<std::tuple<First>>(),
        std::declval<typename RemoveLast<Rest...>::type>()
    ));
};

template<typename First>
struct RemoveLast<First> {
    using type = std::tuple<>;
};

【讨论】:

  • 效果很好!谢谢您的帮助。 (花了一段时间才开始尝试——抱歉。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 2015-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-19
相关资源
最近更新 更多