【问题标题】:Forward a function into a lambda-expression in c++11将函数转发到 c++11 中的 lambda 表达式
【发布时间】:2016-06-15 21:36:39
【问题描述】:

对于 C++11 中的 Packaged_Task 实现 我想实现我在下面的 C++14 代码中表达的内容。换句话说,我想转发到一个 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 表达式)

【问题讨论】:

  • 不清楚,你是说f 不可复制吗?
  • 是的,f 不可复制构造,因为它捕获了唯一的对象

标签: c++ c++11 lambda


【解决方案1】:

如何创建一个在复制构造期间执行移动的包装器结构:(。(我知道这很糟糕,让我想起了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 本身还是在某个博客上。

【讨论】:

    【解决方案2】:

    首先,让我们把问题归结为核心:函数对象有点让人分心。本质上,您希望能够创建一个 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&lt;T&gt; 包装器实际上只是捕获参数的传递方式:如果传入的是左值,则捕获左值。为了正确获取包含的对象,extract() 成员 std::forward&lt;T&gt;()s 对象:该函数只能安全地调用一次,因为对象可能会从那里移动。

    【讨论】:

      【解决方案3】:

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

      相反,我们可以将移动后的值作为参数传递给 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 是同一解决方案的更复杂版本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-24
        相关资源
        最近更新 更多