将函数转发到c ++ 11

时间:2016-06-15 06:46:47

标签: c++ c++11 lambda

对于C ++ 11中的Packaged_Task实现 我想实现我在C ++ 14 Code中所表达的内容。换句话说,我想转发一个lambda表达式。

template<class F>
Packaged_Task(F&& f) {
    Promise<R> p;
    _future = p.get_future();

     auto f_holder = [f = std::forward<F>(f)]() mutable { return std::move(f); };
 ///...

我知道进入lambda的变通方法(但是这个变通方法需要一个默认的可构造对象,在我的例子中,对象通常是没有默认构造函数的lambda表达式)

3 个答案:

答案 0 :(得分:3)

如何创建一个在复制构造过程中移动的包装结构:(。(我知道这很糟糕,让我记住auto_ptr

template <typename F>
struct Wrapped {
  using Ftype = typename std::remove_reference<F>::type;
  Wrapped(Ftype&& f): f_(std::move(f)) {}

  Wrapped(const Wrapped& o): f_(std::move(o.f_)) {}

  mutable Ftype f_; 
};

template<class F>
Packaged_Task(F&& f) {
    Promise<R> p;
    _future = p.get_future();
     Wrapped<std::remove_reference<decltype(f)>::type> wrap(std::forward<F>(f));

     auto f_holder = [wrap]() mutable { return std::move(wrap.f_); };

这只是一个粗略的想法。未编译或测试。

注意:我之前看过这种技术,不记得它是在SO本身还是在博客上。

答案 1 :(得分:3)

首先,让我们把问题归结为它的核心:函数对象有点分散注意力。从本质上讲,您希望能够创建一个带有捕获的lambda,该捕获包含一个只移动的对象。在C ++ 11中,没有直接支持,这引起了C ++ 14方法的允许规范如何构建捕获。

对于C ++ 11,有必要使用副本。由于底层类型不支持复制,因此有必要实际移动对象而不是复制它。这样做可以通过合适的包装器来实现,该包装器定义了一个不是真正复制而是移动的复制构造函数。以下示例显示:

#include <utility>

struct foo {
    foo(int) {}
    foo(foo&&) = default;
    foo(foo const&) = delete;
};

template <typename T>
class move_copy
{
    T object;
public:
    move_copy(T&& object): object(std::move(object)) {}
    move_copy(move_copy& other): object(std::move(other.object)) {}

    T extract() { return std::forward<T>(this->object); }
};

template <typename T>
void package(T&& object)
{
    move_copy<T> mc(std::forward<T>(object));
    foo g = [mc]() mutable { return mc.extract(); }();
}

int main()
{
    foo f(0);
    package(std::move(f));
}

move_copy<T>包装器实际上只是以传递方式捕获参数:如果传入左值,则捕获左值。为了正确获取所包含的对象,extract()成员std::forward<T>()是对象:该函数只能安全地调用一次,因为对象可能会从那里移动。

答案 2 :(得分:1)

通过使它成为一个移动来打破复制语义是一个坏主意。如果这是唯一的选择,那就去吧,但事实并非如此。

相反,我们可以将移动的值作为参数传递给lambda,并将其移动到包装代码中。

curry_apply获取一些值和一个函数对象,并返回该值与第一个参数绑定的值的函数对象。

template<class T, class F>
struct curry_apply_t {
  T t;
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  -> typename std::result_of_t<F&(T&, Args...)>::type
  {
    return f(t, std::forward<Args>(args)...);
  }
};
template<class T, class F>
curry_apply_t<typename std::decay<T>::type, typename std::decay<F>::type>
curry_apply( T&& t, F&& f ) {
  return {std::forward<T>(t), std::forward<F>(f)};
}

使用:

template<class F>
Packaged_Task(F&& f) {
  Promise<R> p;
  _future = p.get_future();

  auto f_holder = curry_apply(
    std::move(_future),
    [](Future<R>& f) mutable { return std::move(f); };
  );

基本上我们将移植的数据存储在lambda的中,并将其存储在手动编写的函数对象中。然后我们将它作为一个左值参数传递给lambda参数列表的前面。

Here是同一解决方案的更复杂版本。