【问题标题】:C++ create an std::packaged_task of a generic std::bindC++ 创建一个通用 std::bind 的 std::packaged_task
【发布时间】:2015-10-14 20:48:44
【问题描述】:

我只是尝试为给定的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: 在 'void A::execute(T) [with T = std::_Bind(int)>]': main.cpp:20:20: 必需 从这里 main.cpp:10:36: 错误: 'std::packaged_task(int)>> 任务'有 不完全类型 std::packaged_task task(func);

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

有人有想法吗?

谢谢,

【问题讨论】:

  • 应该如何调用func?如果有的话,它应该返回什么?
  • auto 不允许在参数声明中。
  • 问题是它不能编译
  • T.C,它应该返回任何东西,取决于 std::bind
  • 我在您的代码中看到了一堆与您的问题几乎完全无关的问题。您对 auto 的使用不符合 C++11。您对decltype 的使用向我表明您不知道它的作用。我不确定“理论上这应该如何工作”对您意味着什么。你也不明白 T.C. 的问题:传入的func,你希望它怎么调用?你希望传递一个整数吗?没有?一个字符串?三只鸡和一只山羊? std::bind 的结果没有回答问题。

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


【解决方案1】:

从 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()

如果你有一个类 A 与函数 doSomething(int x) 就像你的评论:

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

我们需要把execute函数改成如下(见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 可能会异步运行。

【讨论】:

  • 恐怕这不能编译,main.cpp:10:36: error: 'std::packaged_task<:_bind>(int)> > task' 有不完整的 typestd::packaged_task task(func);
  • 但这正是问题所在。
  • 哦,对不起,您当然必须包含 标头。编辑了我的帖子
  • 是的,你是对的。我现在明白了。似乎std::packaged_task 需要ReturnType(Args...) 形式的模板参数。请参阅我修改后的答案。
  • 恐怕这是不可能的,因为标准没有定义std::bind的返回类型。相反,它只是指定返回类型的要求,其中包括它必须可转换为std::function
【解决方案2】:

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

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。

【讨论】:

  • std::move(func)(decltype(args)(args)...);我不明白这个语句是什么以及如何形成和使用的??????你能解释一下为什么 twi (args) 以及在哪里使用 2 绑定???
  • @ahmedallam decltype(x)x 的类型声明为。 decltype(x)(x)x 转换为它被声明为的类型。 std::move(func)func 强制转换为右值引用。 std::move(func)( decltype(x)(x) ) 调用func 作为x 的右值,其中x 被强制转换为它声明为的类型。 decltype(args)(args)... 扩展参数包argsstd::move(func)( decltype(args)(args)... )args 扩展为它被声明为的类型并以此调用 func,其中 func 被强制转换为右值引用。
  • 非常感谢。我现在理解了语法,但我想知道你是如何想到解决方案的?解决问题的技术是我希望理解的。我将重读answer.再次感谢+1
  • 如何从函数内部返回任务用于获取future并发布到io_service??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多