【问题标题】:How to move future into lambda-expression如何将未来转移到 lambda 表达式中
【发布时间】:2016-06-14 10:35:43
【问题描述】:

我正在使用 Visual Studio 2013,我想实现这行代码

 f = p.get_future();
 auto task =[f = std::move(f)](){
   //use f
 };

我知道解决方案 here,但不幸的是,这在 VS2013 下无法编译(error C2558 no copy-constructor available)。

【问题讨论】:

  • 获取shared_future并按价值捕获?
  • 带初始化器的捕获是 C++14 的特性,而不是 C++11。
  • 等等,你说的是 lambda 表达式还是 std::function

标签: c++ c++11 lambda move future


【解决方案1】:

您可以使用shared_future。这是最简单的。

但是,这并不能帮助您移动。如果您确实需要移动,我们可以借助 move_helper 函数和类来完成:

template<class T, class F=void>
struct move_helper_t {
  T t;
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  ->typename std::result_of< F&(T&, Args...) >::type
  {
    return f(t, std::forward<Args>(args)...);
  }

  // force right-associativity of `->*`, and
  // invert the stack to get the arguments in the "right" order:
  template<class F1,
    class R0=move_helper_t< T, typename std::decay<F1>::type >
  >
  auto operator->*(F1&& f1)
  -> decltype(
    std::declval<F>()->*
    std::declval<R0>()
  )
  {
    return
      std::move(f)->*
      R0{ std::forward<T>(t), std::forward<F1>(f1) };
  }
};
template<class T>
struct move_helper_t<T,void> {
  T t;
  template<class F>
  auto operator->*(F&& f)
  -> move_helper_t<T, typename std::decay<F>::type>
  {
    return {std::forward<T>(t), std::forward<F>(f)};
  }
};

template<class T>
move_helper_t<std::decay_t<T>>
move_helper( T&& t ) {
  return {std::forward<T>(t)};
}

在 MSVC 2013 中,您可能必须在 move_helper_t 中声明一个构造函数。我不记得他们的return {} 代码写得有多好。

f = p.get_future();
task =
  move_helper(std::move(f)) ->*
  [](std::future<int>& f){
    //use f
  };

-&gt;*move_helper 绑定到 lambda。然后它返回一个可调用对象,该对象将在调用时作为第一个参数传递std::future&lt;int&gt;&amp;

由于它的编写方式,您甚至可以将其链接起来:

auto f = p.get_future();
auto f2 = p2.get_future();
task =
  move_helper(std::move(f)) ->*
  move_helper(std::move(f2)) ->*
  [](std::future<int>& f, std::future<char>& f2){
    //use f
  };

将多个参数移入 lambda。

在这两种情况下,task 都可以由task() 调用——-&gt;* 操作将 lambda 绑定起来并在调用时传递期货。

Live example.

请注意,这解决了将未来移动到 lambda 中的问题。如果您想将 lambda 存储在 std::function 中,这对您没有帮助,因为函数必须是可复制的。

template<class F>
struct shared_function {
  std::shared_ptr<F> pf;
  template<class ...Args>
  typename std::result_of<F&(Args...)>::type
  operator()(Args&&...args) const {
   return (*pf)(std::forward<Args>(args)...);
  }
};
template<class F,
  class dF=typename std::decay<F>::type
>
shared_function< dF >
make_shared_function( F&& f ) {
  return {std::make_shared<dF>(std::forward<F>(f))};
}

这需要一个可移动的 lambda 并将其包装在一个共享指针中并为您公开operator()。但是首先通过上述技术将 future 移动到 lambda 中,然后将该 lambda 包装在共享函数中以将其传递给 std::function 是荒谬的:首先使用 shared_future

顺便说一句,理论上,packaged_task 只需要移动,但我不确定 MSVC2013 是否支持该要求。

【讨论】:

  • 不是你的错,但这绝对是恶心。
  • @LightnessRacesinOrbit 我不知道,我认为我的帖子历史有强有力的证据表明我的运算符重载等的伪装可能是我的错。 :)
  • 好吧,我试图表现得很好;)虽然它是 C++,但不认真。嗯。
猜你喜欢
  • 2015-05-13
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
相关资源
最近更新 更多