In this example a function is passed to an implicitly instantiated function template.
// Function that will be passed as argument
int foo() { return 0; }
//
As has been mentioned before, SFINAE doesn't work because the names of overloaded functions have no definite type in C++, therefore template parameter substitution doesn't even happen at this stage.
However, in your example, the problem is arguably not that you have too many overloads of "foo", but too few overloads of "call". Just provide both the templates with typename F and the ones that expect a function pointer. The compiler will now be able to do the right thing depending on context:
#include <iostream>
// Functions
int foo() { return 0; }
int foo(int) { return 1; }
// Function object
struct Foo
{
int operator()() const { return 2; }
int operator()(int) const { return 3; }
};
// Higher-order functions / templates
template<typename F>
int call(F f) {
return f();
}
int call(int (*f)()) {
return f();
}
template<typename F, typename A>
int call(F f, A a) {
return f(a);
}
template<typename A>
int call(int (*f)(A), A a) {
return f(a);
}
int main()
{
int a = call(foo)
, b = call(foo, 0)
, c = call(Foo())
, d = call(Foo(), 0);
std::cout << a << ',' << b << ',' << c << ',' << d << '\n'; // 0,1,2,3
}
The call overloads can be made more generic by adding return type deduction. In C++11, this is possible even with function objects by using decltype rsp. result_of. For brevity, I will post only the new function signatures, as the bodies don't need to be changed in this case:
template<typename F>
auto call(F f) -> decltype(f());
template<typename R>
R call(R (*f)());
template<typename F, typename A>
auto call(F f, A a) -> decltype(f(a));
template<typename R, typename A>
R call(R (*f)(A), A a);
The closest you can get is probably this:
struct sfoo
{
template<typename... args>
void operator() (args&&... a)
{
foo(std::forward<args>(a)...);
}
};
and pass sfoo
(or sfoo()
) instead of foo
around.
That is, create a function object type that encapsulates the entire overload set in the templatized operator()
.
Then instead of overload resolution over a template argument, which does not exist, you get a template instantiation over the same argument, which is OK.