std::function 是如何为 lambda 构建的

How is std::function constructed for a lambda

我对如何在给定的 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

按值捕获仅移动对象的 lambda 本身变为仅移动,这是有道理的,因为它包含所述对象。

但是std::function has to be copy-constructible and copy-assignable,也就是说它只能包含可复制的对象

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

std::function 是可复制类型。

标准规定,复制时也会复制其内容。

"But",你说,"I never copy it. Why should it need be copied?" 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()>.

的玩具示例

复制的操作存储pimpl->clone()方法中。它必须被编译 即使你从未调用它.

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:

更便宜
std::function<void()> f = [fu=fu.share()]{ /* code */ };