将返回 std::future 的函数调整为<T> std::future<U>

Adapting a function that returns std::future<T> to std::future<U>

本文关键字:lt gt future std 调整 返回 函数      更新时间:2023-10-16

假设我有一个异步函数映射原始映射,它将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_map来构建类似的async_for_each功能:

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 s之间进行转换(或将std::future<T>施加到std::future<void>),但是这个问题的答案表明这是不可能的。

随机想法:

  • async_for_each可以以一种巧妙的方式包装其功能来解决此问题?
  • Container使用的类型可以像async_for_each中的void一样起作用,但可以像async_map中的Container一样行动?

我的最初尝试在下面。有可能在这些约束下构建我想要的东西吗?

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

我认为您正在使用错误的原始。

在这里,我用不同的原始构建一切 - 水槽。

水槽可以通过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别名。

上面的代码尚未进行测试或运行,因此其中可能有错误。我不确定存在一个问题 - 在这种情况下,兰伯达能否以return void_func()返回void? - 但是由于丑陋在一个位置,即使它不起作用也可以解决。