C ++创建一个通用std :: bind的std :: packaged_task

时间:2015-10-14 20:48:45

标签: c++ c++11 c++14 generic-programming

我只是尝试为给定的std::packaged_task创建std::bind

#include <functional>
#include <future>

class A
{
public:
  template<class T>
  void execute(T func)
  {
    std::packaged_task<T> task(func);
  }
};

int main()
{
  auto func = std::bind([](int x) { return x*x; }, 5);

  A name;

  name.execute(func);
}
  

main.cpp:实例化'void A :: execute(T)[with T =   std :: _ Bind(int)&gt;]':main.cpp:20:20:必需   从这里main.cpp:10:3​​6:错误:   “的std :: packaged_task(IN​​T)&GT; &GT;任务'有   不完全类型        std :: packaged_task task(func);

我正在使用G ++ 5.2.0和C ++ 14。

有人有想法吗?

谢谢,

2 个答案:

答案 0 :(得分:3)

从c ++ 11开始,不可能将函数参数声明为auto(我相信它是c ++ 17的一部分)。不幸的是我没有支持该功能的编译器,我不太确定它的语义,但解决方法是使用模板:

#include <future>
#include <functional>

template<class T>
void execute(std::function<T> func) // is a std::bind
{
    std::packaged_task<T> task(func);
    //...
}

你可以这样使用:

std::function<int()> func = std::bind([](int x) { return x*x; }, 5);
execute(func);

出现此错误的原因是std::packaged_task仅适用于ReturnType(Args...)形式的模板参数。因此,如果您传递的内容与std::_Binderstd::bind返回的std::function不同,则会显示为未定义。

修改

关于推断std :: function类型的评论:

您可以使用lambdas而不是std::bind()

如果你的评论中有一个A课程,其功能为doSomething(int x)

class A
{
public:
    int doSomething(int x)
    {
        return x *x;
    }
};

我们需要将执行功能更改为以下内容(请参阅Yakk的回答):

template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
    using zF = std::decay_t<F>&&;
    using R = std::result_of_t< zF(Args...) >;
    std::packaged_task<R()> task(
        std::bind([func = std::forward<F>(func)](auto&&...args)mutable->R{
        return std::move(func)(decltype(args)(args)...);
    }, std::forward<Args>(args)...)
        );
}

现在你可以像这样使用它:

A a;

auto f = [](A& inst, int x) {  return inst.doSomething(x); };
execute(f, a, 5);

// or

auto g = [&]() { return a.doSomething(5); };
execute(g);

注意

请注意a的生命周期,因为packaged_task可以异步运行。

答案 1 :(得分:1)

这是一个简单的解决方案:

template<class...Args, class F>
void execute(F&& func)
{
  using R = std::result_of_t< std::decay_t<F>(Args...) >;
  std::packaged_task<R(Args...)> task(std::forward<F>(func));
}

我添加了完美转发,以及传递参数的选项。如果你没有传入参数,它假定传入的callable接受0个参数。

使用示例:

auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);

如果func需要参数,则必须明确传递它们。这是公平的,因为在不知道它会期望什么参数的情况下很难使用std::packaged_task。 ;)

这也编译:

auto func = [](int x) { return x*x; };
A name;
name.execute<int>(func);

但是你打算如何使用这个打包的任务很棘手。

如果您想模仿std::async之类的界面,我们可以这样做:

template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
  using zF = std::decay_t<F>&&;
  using R = std::result_of_t< zF(Args...) >;
  std::packaged_task<R()> task(
    std::bind( [func=std::forward<F>(func)](auto&&...args)mutable->R{
      return std::move(func)(decltype(args)(args)...);
    }, std::forward<Args>(args)... )
  );
}

小心地将传递的func包装在lambda中,以避免在bind上调用bind,然后绑定传入的参数。

现在你可以:

name.execute([](int x){ return x*x; }, 5);

根本不使用bind

上面的代码尚未编译,可能包含tpyos。