【问题标题】:Passing pointer to generic method into template class将指向泛型方法的指针传递给模板类
【发布时间】:2020-04-22 15:42:47
【问题描述】:

我想创建一个模板类,它的构造函数接受一个对象指针和一个指向对象方法之一的指针。模板类必须接受带有任何参数的方法,所以我认为方法的类型应该是模板参数。我也更愿意接受具有任何返回类型的方法,但可以将其限制为返回 void。下面的代码无法编译。什么是正确的语法?

template <typename Obj, typename Method>
class Foo
{
public:
  Foo(Obj *obj, Obj::*Method method)
    :mObj(obj), mMethod(method)
  {}

  void callMethod()
  {
    mObj->mMethod();
  }

private:
  Obj* mObj;
  Obj::*Method mMethod;
};

class Bar
{
public:
  // I want it to work no matter what arguments this method takes.
  void method() {}
};

Bar bar;
Foo <Bar, void(Bar::*)()> foo(&bar, &Bar::method);

我在 Foo 构造函数中收到此错误:

error C2059: syntax error: '<tag>::*'

我在这个主题上的previous question 被标记为重复,但引用的示例指定了可以传递的方法的确切类型,我需要它是通用的。

【问题讨论】:

  • 只是有点语法错误:Method Obj::*method.
  • 你为什么不直接使用std::function和/或std::bind
  • &lt;tag&gt; 是什么?
  • @idclev463035818 MSVC 的解析器感到困惑并吐出一个通用错误字符串。

标签: c++ templates


【解决方案1】:

首先,您的代码中有几个语法问题。此外,您根本不需要 Method 作为模板参数,因为您总是希望传递 Object 类型的成员函数。

要在callMethod 中获取可变数量的参数,只需向Foo 提供可变参数包。也可以推导出callMethod函数的返回类型。

把所有这些放在一起,你可以得到:

template <typename Obj, typename Ret, typename ...Args>
class Foo
{
public:
  Foo(Obj *obj, Ret (Obj::*method)(Args...))
    : mObj(obj), mMethod(method)
  {}

  Ret callMethod(Args... args)
  {
    return (mObj->*mMethod)(args...);
  }

private:
  Obj* mObj;
  Ret (Obj::*mMethod)(Args...);  // this is essentially 'Method'
};

现在,如果你有一个特定函数的参数数量可变的类,它可以正常工作:

class Bar
{
public:
  void method() { std::cout << "bar"; }
};

class Car
{
public:
  int method2(int, double) { 
    std::cout << "car"; 
    return 42;
   }
};

int main()
{
Bar bar;
Car car;
Foo a (&bar, &Bar::method);
Foo b (&car, &Car::method2);
a.callMethod();  // prints 'bar'
b.callMethod(5, 3.2);  // prints 'car'
}

这是一个有效的demo

请注意,在实际解决方案中,您可能希望完美转发参数,但这应该会让您走上正确的轨道。

另外,在c++17之前,没有CTAD(类模板参数推导),所以在构造Foo对象时必须指定模板参数,像这样:

Foo <Bar, void> a(&bar, &Bar::method);
Foo <Car, int, int, double> b(&car, &Car::method2);

【讨论】:

  • 您的可变参数版本等同于另一个版本(Method 只是 void(int, double)),但是您现在已经失去了处理非void 成员函数的能力。编辑:嗯,不等价,但你明白我的意思:ReturnType 参数会很好。
  • 嗯,看起来 OP 可以使用 void 返回类型,但这两种方法都可以:)
  • @cigien 在我的编译器 (Visual Studio 2015) 上,它在声明 Foo 对象时需要模板参数。你怎么能省略它们——这是 gcc 的事情吗?
  • 你需要 c++17。在此之前,没有 class-template-argument-deduction。添加了修复答案。
  • @cigien 这对于 C++11 有什么影响?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多