如何为lambda构造std :: function

时间:2016-05-02 14:28:57

标签: c++11 lambda std-function

我对如何在给定的lambda中构造std::function感到困惑。 std::function的构造函数列在here中。哪一个实际用于捕获一个lambda?是template< class F > function( F f );吗?看起来我无法使用捕获非可复制构造对象的lambda构造std::function。为什么这对lambda捕获是必要的?

// fu is an object of type std::future 
std::function f = [future=std::move(fu)]() {...}  // compile error

// foo is an object of type int
std::function f = [foo=std::move(foo)]() {...} // compile ok

2 个答案:

答案 0 :(得分:3)

简短的回答是标准规定只有可复制的功能对象可以存储在std::function中。这是不能令人满意的:为什么?

std::function是可复制的类型。

该标准规定,复制时,它还会复制其内容。

&#34;但是&#34;,你说,&#34;我从不复制它。为什么需要复制?&#34; std::function实例记录如何复制其内容,即使它从未这样做过。它通常使用一种称为类型擦除的技术。

这是一个玩具示例:

struct invoke_later {
  struct i_impl {
    virtual ~i_impl() {}
    virtual void invoke() const = 0;
    virtual std::unique_ptr<i_impl> clone() const = 0;
  };
  template<class T>
  struct impl:i_impl {
    T t;
    ~impl() = default;
    void invoke() const override {
      t();
    }
    impl(T&& tin):t(std::move(tin)) {}
    impl(T const& tin):t(tin) {}
    virtual std::unique_ptr<i_impl> clone() const {
      return std::make_unique<impl>(t);
    };
  };
  std::unique_ptr<i_impl> pimpl;

  template<class T,
    // SFINAE suppress using this ctor instead of copy/move ctors:
    std::enable_if_t< !std::is_same<std::decay_t<T>, invoke_later>{}, int>* =0
  >
  invoke_later( T&& t ):
    pimpl( std::make_unique<impl<std::decay_t<T>>( std::forward<T>(t) ) )
  {}

  invoke_later(invoke_later&&)=default;
  invoke_later(invoke_later const&o):
    pimpl(o.pimpl?o.pimpl->clone():std::unique_ptr<i_impl>{})
  {}

  ~invoke_later() = default;

  // assignment goes here

  void operator() const {
    pimpl->invoke();
  }
  explicit operator bool() const { return !!pimpl; }
};

以上是std::function<void()>的玩具示例。

复制操作存储->clone()的{​​{1}}方法中。即使您从未将其称为,也必须编译

pimpl规范的编写者,他们了解上述技术,并了解其局限性,并希望仅使用它来实现std。此外,他们希望std::function上的简单操作能够以可预测的方式运行:使用不可复制的内容,复制std::function应该做什么?

请注意,您可以通过将状态包装在std::function中来解决此问题。然后,shared_ptr的副本将只存储您所在州的共享引用,而不是副本。

std::function

现在:

template<class F>
auto shared_state( F&& f ) {
  return [pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))]
    (auto&&... args)->decltype(auto) {
      return (*pf)(decltype(args)(args)...);
    };
}

将编译并工作。

另一种方法是制作不可复制的std::function<Sig> f = shared_state([future=std::move(fu)]() {...}); 并使用该代码而不是std::function

最后,在使用std::function时,future是可复制的shared_future类型,可能比执行future便宜:

shared_state

答案 1 :(得分:1)

通过值捕获仅移动对象的lambda变为自身仅移动,这是有意义的,因为它包含所述对象。

std::function必须是copy-constructiblecopy-assignable,这意味着它只能包含可复制对象。