There is no such killing as busting.
Step 1: write a friendly SFINAE std::result_of and a function that helps to call through the tuple:
namespace details { template<size_t...Is, class F, class... Args> auto invoke_tuple( std::index_sequence<Is...>, F&& f, std::tuple<Args>&& args) { return std::forward<F>(f)( std::get<Is>(std::move(args)) ); }
Now we have invoke_result_t<A(B,C)> , which is SFINAE friendly result_of_t<A(B,C)> can_invoke<A(B,C)> and can_invoke<A(B,C)> , which just does the check.
Then write move_only_function , the version only for moving std::function :
namespace details { template<class Sig> struct mof_internal; template<class R, class...Args> struct mof_internal { virtual ~mof_internal() {};
not tested, just spewed code. can_invoke gives the basic SFINAE constructor - you can add "the return type converts correctly" and "void return type means we ignore the return" if you want.
Now we will recycle your code. First, your task is functions only for movement, not functions:
std::vector<move_only_function<X>> mTasks;
Then we save one of the values โโof type R once and use it again:
template<class F, class... Args, class R=std::result_of_t<std::decay<F>_&&(std::decay_t<Args>&&...)>> std::future<R> push(F&& f, Args&&... args) { auto tuple_args=std::make_tuple(std::forward<Args>(args)...)];
we put the arguments into the tuple, pass that tuple to the lambda, and call the tuple as "only once" in the lambda. Since we will only call the function once, we optimize the lambda for this case.
A packaged_task<R()> compatible with a move_only_function<R()> unlike a std::function<R()> , so we can just move it to our vector. std::future , which we get from it, should work fine, even if we got it before move .
This should slightly reduce overhead. Of course, there are many patterns.
I did not compile any of the codes above, I just spewed it, so the chances that everything compiles are low. But errors should mainly be tpyos.
In random order, I decided to pass move_only_function 4 different () overloads (rvalue / lvalue and const / not). I could add volatility, but that seems reckless. Which admittedly enhances the pattern.
Also in my move_only_function no operation "get at the basic stored stuff", which has std::function . Feel free to type erase if you want. And it processes (R(*)(Args...))0 , as if it were a real function pointer (I return true when I click on bool , and not as null: type erasure of convert-to- bool may be appropriate for the implementation of more industrial quality.
I rewrote std::function because std does not have a std::move_only_function , and the concept is generally useful (as evidenced by packaged_task ). Your solution makes your caller movable by wrapping it with std::shared_ptr .
If you don't like the template described above, consider writing make_copyable(F&&) , which takes an F function object and completes it using your shared_ptr method to make it copyable. You can even add SFINAE to avoid it if it can already be copied (and call it ensure_copyable ).
Then your source code will be cleaner since you just copy packaged_task and then save it.
template<class F> auto make_function_copyable( F&& f ) { auto sp = std::make_shared<std::decay_t<F>>(std::forward<F>(f)); return [sp](auto&&...args){return (*sp)(std::forward<decltype(args)>(args)...); } } template<class F, class... Args, class R=std::result_of_t<std::decay<F>_&&(std::decay_t<Args>&&...)>> std::future<R> push(F&& f, Args&&... args) { auto tuple_args=std::make_tuple(std::forward<Args>(args)...)];
it still requires the invoke_tuple template above, mainly because I don't like bind .