template<class...Sigs> strucct functions:std::function<Sigs>...{ using std::function<Sigs>::operator()...; template<class T, std::enable_if<!std::is_same<std::decay_t<T>,fundtions>{}>,int> =0 > functions(T&&t): std::function<Sigs>(t)... {} };
the above sketch is a C ++ 17 gross object in which a cam stores more than one operator() .
A more efficient one will store an object only once, but save what you call it in many ways. And I missed a lot of details.
This is not really a std::function , but a compatible type; The std function saves only one way to call an object.
Here is a "function representation" that accepts any number of signatures. He does not own the object to be called.
template<class Sig> struct pinvoke_t; template<class R, class...Args> struct pinvoke_t<R(Args...)> { R(*pf)(void*, Args&&...) = 0; R invoke(void* p, Args...args)const{ return pf(p, std::forward<Args>(args)...); } template<class F, std::enable_if_t<!std::is_same<pinvoke_t, std::decay_t<F>>{}, int> =0> pinvoke_t(F& f): pf(+[](void* pf, Args&&...args)->R{ return (*static_cast<F*>(pf))(std::forward<Args>(args)...); }) {} pinvoke_t(pinvoke_t const&)=default; pinvoke_t& operator=(pinvoke_t const&)=default; pinvoke_t()=default; }; template<class...Sigs> struct invoke_view:pinvoke_t<Sigs>... { void* pv = 0; explicit operator bool()const{ return pv; } using pinvoke_t<Sigs>::invoke...; template<class F, std::enable_if_t<!std::is_same<invoke_view, std::decay_t<F>>{}, int> =0> invoke_view(F&& f): pinvoke_t<Sigs>(f)... {} invoke_view()=default; invoke_view(invoke_view const&)=default; invoke_view& operator=(invoke_view const&)=default; template<class...Args> decltype(auto) operator()(Args&&...args)const{ return invoke( pv, std::forward<Args>(args)... ); } };
Living example .
I use C ++ 17 using ... because the binary tree implementation in C ++ 14 is ugly.
For your use case, it will look like this:
auto func_object = [](int i = 0){}; invoke_view<void(), void(int)> f1 = func_object; std::function<void(int)> f3 = f1;
note that the lack of lifecycle management in invoke_view means that the above only works when func_object continues to exist. (If we call the view to represent the call, the "internal" call view is also stored by the pointer, so it should go on, and not the case if we store the call view in the std function).
Managing a goalβs life cycle done right takes a bit of work. You want to use a little buffer optimization with an optional smart pointer or something to get reasonable performance with small lambdas and avoid the heap allocation overhead.
A simple, naive heap allocation solution will replace void* with unique_ptr<void, void(*)(void*)> and save { new T(t), [](void* ptr){static_cast<T*>(ptr)->~T();} } in it (or similar).
This decision makes the function object only to move; copying it also requires a type that erases the clone operation.