【问题标题】:Pass a template variadic function and its arguments to a function将模板可变参数函数及其参数传递给函数
【发布时间】:2019-02-24 18:20:29
【问题描述】:

我希望能够传递一个函数,该函数采用任意数量的参数以及匹配的参数作为函数的参数。这是我迄今为止尝试过的,我迷路了,因为我不太了解参数包的工作原理。

template <class T, typename... Args>
static void BindAction(const std::string& name, T* owner, const std::function<void(T*, Args...)>& func, Args... args)
{
    for (InputAction& action : m_actions)
        if (action.m_name == name)
            action.BindFunction(std::bind(func, owner, args...));
}

我用这些来测试它:

void Test(float f, std::string s, int i);

BindAction<Simulation>("Test", this, &Simulation::Test, 5.f, "a string", 2);

错误只是没有函数模板“BindAction()”的实例与参数列表匹配。

请不要通过告诉我调用std::bind() 然后将结果函数作为参数传递来解决问题。我真的希望电话是这样的。如果这是不可能的,那就太糟糕了,但只有这样我才会检查其他解决方案。

【问题讨论】:

  • 成员函数不是 std::function。函数模板需要(几乎)完全匹配,不会为您插入隐式用户定义的转换。
  • “成员函数不是 std::function”是什么意思?如果我去掉添加参数的概念,我完全可以将成员函数作为 std::function 传递。
  • 我不明白 `return action.BindFunction(std::bind(func, owner, args...));` 那行应该做什么?你只是想调用函数吗?
  • 另外,你考虑的是标准函数还是成员函数?要调用成员函数,需要this 某处,同意吗?
  • 我正在将函数添加到地图中。这部分无关紧要,因为接下来的工作已经开始了。是的,我将 this 作为参数传递给函数,这是“T*”

标签: c++ variadic-templates std-function template-argument-deduction stdbind


【解决方案1】:

您绑定了所有参数,因此您的地图实际上将充满std::function&lt;void()&gt;,它与传入的函数不匹配。您可以将 lambdas 与 std::invoke 耦合以获得更可预测的结果,并且只需使用模板来推断传入的可调用对象的类型:

#include <functional>
#include <iostream>

class Actions {
 public:
  template <typename F, typename... Args>
  void BindAction(const std::string& name, F f, Args... args) {
    actions_.emplace(
      name,
      [f, args...] () mutable { return std::invoke(f, args...); }
    );
  }

  void call(const std::string& name) {
    actions_[name]();
  }

 private:
  std::unordered_map<std::string, std::function<void()>> actions_;
};

还有一个简单的使用例子:

class Cls {
  public:
    void do_func(int i) {
      std::cout << "Cls::do_func(" << i << ")\n";
    }
};

int main() {
  Cls c;
  Actions actions;
  actions.BindAction("test", &Cls::do_func, c, 1);
  actions.call("test");
}

【讨论】:

    【解决方案2】:

    您的代码中存在一些问题。

    排名不分先后

    (1) BindAction() 接收一个std::function&lt;void(T*, Args...)&gt; const &amp;,用于推导出一些TArgs... 类型,但是你将&amp;Simulation::Test 传递给它,所以一个指向(static?) 的指针类/结构的方法,不是std::function

    您传递的指针可以转换为std::function,但不是std::function

    一种鸡蛋和鸡肉的问题:编译器无法将指针转换为std::function,因为不知道Args... 类型,也无法推断Args... 类型,因为没有收到std::function

    (2) 您需要两个不同的参数类型的可变参数列表:std::function 的参数的 Args1... 列表和接收到的参数 (args...) 的第二个 Args2... 列表。

    查看您的通话示例

    void Test(float f, std::string s, int i); 
    
    BindAction<Simulation>("Test", this, &Simulation::Test, 5.f, "a string", 2);
    

    Test() 方法接收一个float、一个std::string 和一个int;但BindAction() 收到floatchar const [9]int

    如果您使用单个 Args... 列表,编译器无法推断类型,因为有两个不同的类型列表。

    (3) 不确定但是...您的 BindAction()static 方法,但使用(如果我理解正确的话)对象的成员 (m_action)

    (4) 你的 BindAction() 方法是 void 方法但返回一个函数 [更正]

    建议:

    (a) 接收作为模板类型本身的可调用对象,而不推断参数的类型

    (b) 接收参数列表作为通用引用并使用模板转发

    为了展示一个简化的例子,如下所示

    #include <functional>
    #include <iostream>
    
    template <typename F, typename ... As>
    static auto BindAction (F const & func, As && ... args)
     { return std::bind(func, std::forward<As>(args)...); }
    
    void Test(float f, std::string s, int i)
     { std::cout << "f[" << f << "], s[" << s << "], i[" << i << "]" << std::endl; } 
    
    int main ()
     { 
       auto ba = BindAction(Test, 5.f, "a string", 2);
    
       std::cout << "post BindAction(), pre ba()\n";
    
       ba();
     }
    

    【讨论】:

      猜你喜欢
      • 2014-10-20
      • 2011-11-15
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-07
      • 1970-01-01
      相关资源
      最近更新 更多