How is std :: function created for lambda

I'm a little confused about how std::function is created on this lambda. The constructor std::function is listed here . Which one is really used to capture lambda? This is template< class F > function( F f ); ? It seems like I cannot build a std::function with a lambda that captures objects that do not support copying. Why is it necessary for lambda capture?

 // 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 
+5
source share
2 answers

The short answer is that standard states can only be saved for objects with the ability to copy to std::function . This is unsatisfactory: why?

std::function is a copied type.

The standard states that when copying, it also copies its contents.

โ€œBut,โ€ you say, โ€œI never copy it. Why do I need to copy it?โ€ The std::function instance records how to copy its contents, even if it never does. He usually uses a method known as type erasure.

Here is an example of a toy:

 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; } }; 

the above toy example is a std::function<void()> .

The copy operation is stored in the ->clone() method of pimpl . It must be compiled, even if you never name it.

The authors of the std specification, who are aware of the above method and know its limitations, and would like std::function be implemented simply with its help. In addition, they wanted simple operations on std::function done in predictable ways: with non-copied content, what should std::function do copy?

Note. You can work around this problem by wrapping your state in shared_ptr . Then copies of your std::function will simply store shared references to your state, not copies.

 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)...); }; } 

Now:

 std::function<Sig> f = shared_state([future=std::move(fu)]() {...}); 

will compile and work.

An alternative approach is to make std::function not copied and use it instead of std::function .

Finally, when working with future , a shared_future is a copied type of future and may be cheaper than doing shared_state :

 std::function<void()> f = [fu=fu.share()]{ /* code */ }; 
+3
source

A lambda that captures an object only for moving by value becomes a movement in itself, which makes sense because it contains the specified object.

But std :: function must be copy-constructible and copy-assignable , which means that it can only contain objects that are being copied.

+1
source

All Articles