Why cannot F
be deduced for proxy()
?
It should be possible because I am restricting it - only for functions that return an int
.
#include <utility>
#include <iostream>
#include <type_traits>
using namespace std;
int foo(int bar) {
cout << "int" << endl;
return 2;
}
float foo(float bar) {
cout << "float" << endl;
return 1;
}
template <typename F, typename... Args>
typename enable_if<
is_same<
typename result_of<F(Args...)>::type,
int
>::value,
typename result_of<F(Args...)>::type
>::type
proxy(F func, Args&&... args) {
return func(forward<Args>(args)...);
}
int main() {
proxy(foo, 5);
}
Here is the error:
b.cpp:29:17: error: no matching function for call to 'proxy(<unresolved overloaded function type>, int)'
b.cpp:24:1: note: template argument deduction/substitution failed:
b.cpp:29:17: note: couldn't deduce template parameter 'F'
The problem is this:
proxy(foo, 5);
The compiler tries to deduce the type of foo
, but there are 2 overloads. Of course, it can deduce Args...
from 5
, but the type of foo
is still non-deducible, since the compiler doesn't know which overload to pick when doing the type deduction.
Note that the compiler needs to know the type of F
in the signature of the function, i.e. here, so SFINAE do its magic:
is_same<
typename result_of<F(Args...)>::type,
int
>::value,
There is absolutely no way for it to correctly deduce the type of F
from the proxy(foo, 5)
call, so SFINAE cannot kick in. As a side note, remark that C++ cannot overload based on the return type only. So you won't be able to differentiate two functions with the same name based on the return type alone. You'll need somehow to force a parameter matching which will SFINAE out the non-candidate overloads.
Somehow related: Deducing the return type of a standalone function
And a relevant quote from the standard, emphasize mine (thanks to @T.C. for pointing it out):
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]/(6.2)
(6) When P is a function type, pointer to function type, or pointer to member function type:
(6.1) If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
(6.2) If the argument is an overload set (not containing function
templates), trial argument deduction is attempted using each of the
members of the set. If deduction succeeds for only one of the overload
set members, that member is used as the argument value for the
deduction. If deduction succeeds for more than one member of the
overload set the parameter is treated as a non-deduced context.
In your example foo
names a set of overloaded functions, and template argument deduction is unable to select one overload over another because they're both matches of equal rank.
Your SFINAE constraint check doesn't kick in until after F
has been deduced, so it's of no help in dropping float foo(float)
from the overload resolution set.
Say you renamed the function returning float
to foof
, then your example would compile. But if you tried calling proxy
with foof
as the function argument, the code would again fail to compile, this time because of the enable_if
constraint.
To get your example to compile in its current state you must disambiguate which foo
you are passing to proxy
proxy(static_cast<int(*)(int)>(foo), 5);