c++ - 将 lambda 表达式传递给创建异步的函数

标签 c++ lambda task future

我正在尝试创建一个通过实例化异步来使用函数的类。那是我的代码:

main.cpp

MPIAsyncPool pool;

MPIFuture val = pool.addTask(launch::async, [](int a) -> int {
   return a;
}, 7);
cout << "Value: " << val.get() << endl;

MPIFuture v = pool.addTask(std::launch::async, doThings, 46);
cout << "Value: " << v.get() << endl;

MPIAsyncPool(.h&.cpp)

class MPIAsyncPool {
public:
    MPIAsyncPool();
    template<typename T, typename U>
    MPIFuture addTask(std::launch, std::function<T(U...)> f, U...);
};

template<typename ReturnType, typename ArgsTypes>
MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function<ReturnType(ArgsTypes...)> f, ArgsTypes... args) {
    std::future<int> fut = std::async(launch, f, args);
    return MPIFuture(fut.get());
}

声明似乎没问题,但定义签名有点问题。我错了吗?

这些是我得到的错误:

/home/quero/ClionProjects/MPIAsyncPool/main.cpp: In function ‘int main(int, char**)’:
/home/quero/ClionProjects/MPIAsyncPool/main.cpp:17:15: error: no matching function for call to ‘MPIFuture::MPIFuture()’
     MPIFuture val;
               ^
In file included from /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:11:0,
                 from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3:
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:12:5: note: candidate: MPIFuture::MPIFuture(int)
     MPIFuture(int);
     ^
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:12:5: note:   candidate expects 1 argument, 0 provided
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate: constexpr MPIFuture::MPIFuture(const MPIFuture&)
 class MPIFuture {
       ^
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note:   candidate expects 1 argument, 0 provided
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate: constexpr MPIFuture::MPIFuture(MPIFuture&&)
/home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note:   candidate expects 1 argument, 0 provided
/home/quero/ClionProjects/MPIAsyncPool/main.cpp:20:13: error: no matching function for call to ‘MPIAsyncPool::addTask(std::launch, main(int, char**)::, int)’
         }, 7);
             ^
In file included from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3:0:
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: candidate: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...)
     MPIFuture addTask(std::launch, std::function f, U...);
               ^
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note:   template argument deduction/substitution failed:
/home/quero/ClionProjects/MPIAsyncPool/main.cpp:20:13: note:   ‘main(int, char**)::’ is not derived from ‘std::function’
         }, 7);
             ^
/home/quero/ClionProjects/MPIAsyncPool/main.cpp:22:64: error: no matching function for call to ‘MPIAsyncPool::addTask(std::launch, int (&)(int), int)’
     MPIFuture v = pool.addTask(std::launch::async, doThings, 46);
                                                                ^
In file included from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3:0:
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: candidate: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...)
     MPIFuture addTask(std::launch, std::function f, U...);
               ^
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note:   template argument deduction/substitution failed:
/home/quero/ClionProjects/MPIAsyncPool/main.cpp:22:64: note:   mismatched types ‘std::function’ and ‘int (*)(int)’
     MPIFuture v = pool.addTask(std::launch::async, doThings, 46);
                                                                ^
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:14:109: error: expansion pattern ‘ArgsTypes’ contains no argument packs
 MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function f, ArgsTypes... args) {
                                                                                                             ^
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:14:11: error: prototype for ‘MPIFuture MPIAsyncPool::addTask(std::launch, std::function)’ does not match any in class ‘MPIAsyncPool’
 MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function f, ArgsTypes... args) {
           ^
In file included from /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:5:0:
/home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: error: candidate is: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...)
     MPIFuture addTask(std::launch, std::function f, U...);

最佳答案

std::function 是一种类型,其目的是类型删除,允许您在同一类型的存储中存储多个类型的可调用值。你没有这样做。

模板类型推导是当您从函数调用的参数推导模板参数时发生的事情。你正在做这个。

类型删除和类型推导相反。您的代码试图推断要删除的类型,这在实践中不起作用,理论上也很少有意义。

并且,类型删除发生在std::async中:每一层类型删除都有成本(运行时和编译时),所以只有在您需要在同一个变量中存储关于不止一种类型的信息。

这是您的代码的第一遍:

class MPIAsyncPool {
public:
  MPIAsyncPool();
  // R is the return type of F when invoked with Us...
  template<class F, class...Us, class R=typename std::result_of<F&(Us...)>::type>
  MPIFuture addTask(std::launch, F&& f, Us&&...);
};

也在.h文件中:

template<class F, class...Us, class R>
MPIFuture MPIAsyncPool::addTask(std::launch launch, F&& f, Us...us) {
  std::future<R> fut = std::async(launch, std::forward<F>(f), std::forward<Us>(us)...);
  return MPIFuture(fut.get());
}

现在,这仍然很糟糕,因为当您返回时,您阻塞异步。异步的全部要点是在您需要之前不阻塞。

改变:

  return MPIFuture(fut.get());

  return MPIFuture(std::move(fut));

这可能需要修复 MPIFuture,但这是有意义的签名。你想获取 std::future 的状态,而不是阻塞并获取它的值。

最后,您不能在 .cpp 文件中定义模板并在该 .cpp 文件之外使用它们。这是类型删除的用法!

如果您真的想要将实现放在.cpp 文件中,并且愿意将您的代码限制为返回int 的代码,我们可以这样做:

class MPIAsyncPool {
public:
  MPIAsyncPool();
  template<class F, class...Us, class R=typename std::result_of<F&(Us...)>::type>
  MPIFuture addTask(std::launch l, F f, Us...us) {
    // does not support move-only f or us...:
    return addTaskInternal(l, [=]()->int{
      return std::move(f)(std::move(us)...);
    });
  }
private:
  MPIFuture addTaskInternal(std::launch, std::function<int()>);
};

在 .cpp 中:

MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function<int()> f) {
  std::future<int> fut = std::async(launch, std::move(f));
  return MPIFuture(std::move(fut));
}

现在头文件转发到使用非模板签名的内部方法。我们对 lambda 进行类型删除,以便我们只记得如何调用它、销毁它和复制它。这种删除使得将其变成“普通”方法(而不是模板方法)成为可能。

关于c++ - 将 lambda 表达式传递给创建异步的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31413150/

相关文章:

c++ - 使用字符数组写入标准输出(非空终止)c/c++

c++ - VS 2010 中的 Lambda 表达式与 gcc 相对

c# - 具有 boolean 值的 Lambda 查询 GroupBy

c++ - 从 std::map 中删除 std::function lambda-wrapped 方法

javafx - 对话框关闭时取消任务

Django + 执行异步进程?

c++ - 从相同的硬编码字符串文字初始化 std::string 和 std::wstring

C++赋值运算符解析

c# - 在循环 : how to pass values that can be changed inside the loop? 内启动任务

c++ - malloc 没有按我的预期工作