使用std::async
,我想知道是否可以有一个辅助函数,它从集合中创建std::future
(每个集合元素一个未来)。
我经常遇到以下情况:
auto func = []( decltype(collection)::reference value ) {
//Some async work
};
typedef std::result_of<decltype(func)>::type ResultType;
std::vector<std::future<ResultType>> futures;
futures.reserve(collection.size());
// Create all futures
for( auto& element : collection ) {
futures.push_back(std::async(func, element));
}
// Wait till futures are done
for( auto& future : futures ) {
future.wait();
}
为了能够轻松重用它,我想出了以下部分代码:
template< class Function, class CT, class... Args>
std::vector<std::future<typename std::result_of<Function(Args...)>::type>>
async_all( Function&& f, CT& col ) {
typedef typename std::result_of<Function(Args...)>::type ResultType;
std::vector<std::future<ResultType>> futures;
futures.reserve(collection.size());
for( auto& element : collection ) {
futures.push_back(std::async(func, element));
}
}
return futures;
现在我必须解决Args
问题,因为在async_all
,Args
不能再推导了。我目前唯一能想到的是另一个函子,它将集合中的元素转换为Args
。还有比这更优雅的解决方案吗?
你快到了。 传递给 async_all
的集合具有唯一确定函数参数类型所需的所有信息;唯一的问题是如何提取这些信息。在函数签名中使用 auto
关键字,我们可以在函数参数后编写返回类型。 这不仅会产生更干净的签名,而且还允许我们在推导返回类型时将参数值本身与decltype
一起使用。 例如:
template<typename F, typename CT>
auto reduce(F f, CT coll) -> decltype(f(*begin(coll), *begin(coll));
当然,还有其他方法可以确定所提供函数的参数类型(使用带有模板的函数签名推导)。但是,对于涉及重载函数和/或模板化函数对象的情况,这些方法可能会失败。
以下代码在 gcc 4.8 下正确编译和运行(打印"x=1"10 次)(早期版本应该可以正常工作)。请注意,我们甚至不必明确提及std::future
:我们可以直接在std::async
语句上使用decltype来推断它的类型。
#include <future>
#include <vector>
#include <iostream>
template<class Function, class CT>
auto async_all(Function f, CT col)
-> std::vector<decltype(std::async(f, *std::begin(col)))>
{
std::vector<decltype(std::async(f, *std::begin(col)))> futures;
futures.reserve(col.size());
for (auto& element : col) {
futures.push_back(std::async(f, element));
}
return futures;
}
int main()
{
using namespace std;
for (auto& f : async_all([](int x) { cout << "x = " << x << endl; },
vector<int>(10, 1)))
f.get();
}
(async_all
在这里只计算一次,因为规范保证了基于范围的 for 循环中的范围表达式)