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