I've been looking around SO for a while now but cannot find quite the answer that I am looking for - this question is probably the closest to what I am thinking.
In a sentence: is it possible to declare a template function that takes a parameter of an std::function
and deduce template parameters for both the return type and the parameter types of the function? Example:
//this works to pass the std::function in
template<class T>
void doSomething(std::function<T> f) {
f();
}
//this is more what i am looking for - can R and P be deduced automatically - does not work!
template<class R, class P>
void doSomethingElse(std::function<R(P)> f) {
f();
}
Is this because the function signature or function type is considered one thing in itself, and as such cannot be "broken" up? I realise there are decltype
and std::result_of
but cannot think how I might use them here.
As an additional point, how might I extend the second example to have multiple parameters and deduction, using variadic templates?
template<class R, class P>
void doSomethingElse(std::function<R(P)> f) {
f(P{});
}
Will work, but it only works if you pass a std::function
to the function and that function has one non void parameter. This is kind of limiting though. You can use
template<class R, class... Args, class... Ts>
void doSomethingElse(std::function<R(Args...)> f, Ts&&... args) {
f(std::forward<Args>(args)...);
}
Which will take any std::function
and the arguments for it and calls them as if you did it in the call site. This is still limiting though because the call site requires you use a std::function
so you can't pass it anything implicitly convertible to a std::function
.
With C++17 and class template argument deduction (CTAD) this is no longer an issue though. We can create an overload that takes any type, and then construct a std::function using CTAD to fill in the types for us. That would look like
template<class Func, class... Args>
void doSomethingElse(Func&& f, Args&&... args) {
doSomethingElse(std::function{std::forward<Func>(f)}, std::forward<Args>(args)...);
}
template<class R, class... Args, class... Ts>
void doSomethingElse(std::function<R(Args...)> f, Ts&&... args) {
f(std::forward<Args>(args)...);
}
And now anything that isn't a std::function
will go to void doSomethingElse(Func&& f, Args&&... args)
, get converted to a std::function
, and get passed to void doSomethingElse(std::function<R(Args...)> f, Args&&... args)
so you can use the return type and argument(s) type(s) in there.