假设我有一个异步功能映射原语,它以std::vector
作为输入,并将std::future
返回到我选择的Container
作为输出:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
我希望通过调整async_for_each
来构建类似的async_map
函数:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f);
问题在于async_for_each
会返回std::future<void>
,而async_map
会返回std::future<Container>
,void
也不会是Container
。
我可以通过构建满足Container
要求的类型但忽略对它的赋值(在我的初始尝试中empty_container
)来获得接近我想要的东西,但是std::future
此类型仍然不是std::future<void>
。
我对我的解决方案有以下限制:
async_map
的一个实现,具有给定的函数签名(即,没有async_map<void>
特化)std::future
(即没有.then()
式继续)我希望有一种有效的方式来转换std::future
相关类型(或将std::future<T>
转换为std::future<void>
),但this question的答案表明这是不可能的。
随意的想法:
async_for_each
以巧妙的方式包装其功能来解决这个问题吗?Container
的类型可以像void
中的async_for_each
一样,但在Container
中的行为类似async_map
吗?我的初步尝试如下。考虑到这些限制,是否有可能构建我想要的东西?
#include <future>
#include <vector>
#include <iostream>
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
struct empty_container
{
empty_container(size_t) {}
struct empty
{
template<class T>
empty operator=(const T&) const { return empty(); }
};
empty operator[](size_t) { return empty(); }
};
template<class Function>
struct invoke_and_ignore_result
{
Function f;
template<class T>
empty_container::empty operator()(T&& x) const
{
f(std::forward<T>(x));
return empty_container::empty();
}
};
template<class T, class Function>
//std::future<void> async_for_each(const std::vector<T>& in, Function f)
std::future<empty_container> async_for_each(const std::vector<T>& in, Function f)
{
invoke_and_ignore_result<Function> g{f};
std::future<empty_container> f1 = async_map<empty_container>(in, g);
return f1;
}
int main()
{
std::vector<int> vec(5, 13);
async_for_each(vec, [](int x)
{
std::cout << x << " ";
}).wait();
std::cout << std::endl;
return 0;
}
答案 0 :(得分:6)
我认为你使用的是错误的原语。
在这里,我用不同的原语构建一切 - 一个接收器。
接收器可以通过operator()(T&&)&
消耗数据。然后它通过operator()()&&
返回一些结果。
这是一个async_sink
函数:
template<class Container, class Sink>
std::future<std::result_of_t<std::decay_t<Sink>()>>
async_sink(Container&& c, Sink&& sink)
{
return std::async(
[c=std::forward<Container>(c), sink=std::forward<Sink>(sink)]
{
for( auto&& x : std::move(c) ) {
sink( x );
}
return std::move(sink)();
});
}
以下是sink
的实现,它将事物放入容器中,然后将其返回:
template<class C>
struct container_sink_t {
C c;
template<class T>
void operator()( T&& t ){
c.emplace_back( std::forward<T>(t) );
}
C operator()()&&{
return std::move(c);
}
};
这是一个接收函数和接收器并构成它们的接收器:
template<class F, class S>
struct compose_sink_t {
F f;
S s;
template<class T>
void operator()(T&& t){
s(
f(std::forward<T>(t))
);
}
std::result_of_t<S()> operator()()&&{
return std::move(s)();
}
};
template<class C, class F>
compose_sink_t<std::decay_t<F>, container_sink_t<C>>
transform_then_container_sink( F&& f ) {
return {std::forward<F>(f)};
}
这是一个接收函数,调用它并返回void
:
template<class F>
struct void_sink_t {
F f;
template<class T>
void operator()(T&& t)
{
f(std::forward<T>(t));
}
void operator()() {}
};
template<class F>
void_sink_t<std::decay_t<F>> void_sink(F&&f){return {std::forward<F>(f)}; }
现在你的地图是:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return async_sink(
in,
transform_then_container_sink<Container>(std::forward<F>(f))
);
}
你的for_each是:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f)
{
return async_sink(
in,
void_sink(std::forward<F>(f))
);
}
我自由使用C ++ 14功能,因为它们使代码更好。您可以使用副本替换move-into-container,以降低效率,并编写自己的_t
别名。
以上代码尚未经过测试或运行,因此可能存在漏洞。有一个我不确定的问题 - 在该上下文中,lambda返回void是否以return void_func()
结尾? - 但是因为丑陋在一个地方,即使它不起作用也可以解决。