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;
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()]{ };