C++ 创建泛型 std::bind 的 std::packaged_task

C++ create an std::packaged_task of a generic std::bind

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

#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: In instantiation of 'void A::execute(T) [with T = std::_Bind(int)>]': main.cpp:20:20: required from here main.cpp:10:36: error: 'std::packaged_task(int)> > task' has incomplete type std::packaged_task task(func);

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

有人有想法吗?

谢谢,

从 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::bind 返回的 std::_Binderstd::function,它会显示为未定义。

编辑

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

您可以使用 lambda 代替 std::bind()

如果您有一个 class 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 可能 运行 异步。

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

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));
}

我添加了完美转发,以及传入参数的选项。如果您不传入参数,它会假定传入的可调用函数采用 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。