【问题标题】:How to extract the arguments of a function pointer as type parameter pack?如何将函数指针的参数提取为类型参数包?
【发布时间】:2021-02-18 19:14:36
【问题描述】:

我愿意支持这样的电话

class BaseClass {
public:
  virtual void print2(float arg1, float arg2) = 0;
};

int main() {
  invoke(subclass, &BaseClass::print2, 1, 2);      
}

在哪里

  1. subclass 自动向上转换为 BaseClass
  2. 参数自动从int 转换为float
  3. 使用错误参数类型/类类型的调用会导致错误

我设法用基类解决了这部分问题。 但是争论的部分让我很头疼。 我的方法是从传递的函数指针中提取所有需要的类型信息。 然后(如果可能)隐式转换其他参数。

到目前为止,我所拥有的是:

template<class Func> struct FunctionPointer2 {};

template<class Class, typename Arg1, typename Arg2> struct FunctionPointer2<void (Class::*)(Arg1, Arg2)> {
  typedef Class Class;
  typedef Arg1 Argument1;
  typedef Arg2 Argument2;
};

template <typename Func>
void invoke2(typename FunctionPointer2<Func>::Class* obj, Func func, typename FunctionPointer2<Func>::Argument1 arg1, typename FunctionPointer2<Func>::Argument2 arg2) {
  (obj->*func)(arg1, arg2);
}

它可以工作,但是对于每个参数计数,需要另一个函数,函数指针结构,...。

所以问题来了:追随甚至可能吗?我怎样才能使它成为可能?

template<class Func> struct FunctionPointer {};

template<class Class, typename ... Args> struct FunctionPointer<void (Class::*)(Args ...)> {
  typedef Class Class;
  typedef Args Arguments; // pass type parameter pack here
};

template <typename Func>
void invoke(typename FunctionPointer<Func>::Class* obj, Func func, typename FunctionPointer<Func>::Arguments ... params) {
  (obj->*func)(params ...);
}

感谢您的帮助:)

【问题讨论】:

  • 不幸的是,我不能。我已经将我的问题简化为这个,但背景要复杂得多。具体来说,我需要它用于 Qt 中的异步、跨线程函数调用。
  • 在您回复之前删除了我的评论。我相信你不应该需要提取参数类型,看看答案。

标签: c++ templates variadic-templates


【解决方案1】:

手动向上转换和提取参数类型都应该是 没必要。

你可以这样做:

template <typename T, typename M, typename ...P>
void invoke(T &&object, M member, P &&... params)
{
    (std::forward<T>(object).*member)(std::forward<P>(params)...);
}

【讨论】:

  • object 上的std::forward 真的有必要吗?我见过forward 在传递参数时使用,但在访问实例时没有。那么,(object.*member)(std::forward&lt;P&gt;(params)...) 不行吗?
  • @RemyLebeau 在大多数情况下它不会产生影响,但请记住成员函数可以是 ref-qualified。
  • "成员函数可以被 ref 限定" - 很好,谢谢。
  • 虽然很容易,但为什么要让它变得困难?没有考虑使用标准库来确保类型安全
【解决方案2】:

不确定...但如果您希望第一个参数被向上转换...我想您正在寻找的东西

template <typename D, typename R, typename B, typename ... As1,
          typename ... As2>
void invoke (D && d, R(B::*pnt)(As1...), As2 && ... args)
 { (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
      (std::forward<As2>(args)...); }

注意有几个可变参数模板包:As1...,用于方法所需的参数,As2...,用于传递给args...args...

你可以按如下方式统一这个包

template <typename D, typename R, typename B, typename ... As>
R invoke (D && d, R(B::*pnt)(As...), As && ... args)
void { (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
          (std::forward<As>(args)...); }

但是很危险,因为当没有完美匹配时,编译器可能无法推断出 As... 类型(如在您的示例中,您将两个 int 传递给需要两个 @ 的方法987654329@)。

以下是完整的编译示例

#include <iostream>

struct BaseClass
 { virtual void print2 (float, float) = 0; };

struct Derived : public BaseClass
 {
   void print2 (float f1, float f2)
    { std::cout << f1 << ", " << f2 << std::endl; }
 };


template <typename D, typename R, typename B, typename ... As1,
          typename ... As2>
void invoke (D && d, R(B::*pnt)(As1...), As2 && ... args)
 { (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
      (std::forward<As2>(args)...); }

int main()
 {
   Derived der;

   invoke(der, &BaseClass::print2, 1, 2);      
 }

【讨论】:

    猜你喜欢
    • 2015-09-04
    • 2017-04-12
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 2014-01-17
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    相关资源
    最近更新 更多