I have a template function that should take a function pointer and arguments, then invoke the function pointer with the given arguments (let's call it Invoke
). However, when I call the template function with an overloaded function as an argument, template deduction fails.
I have used enable_if so that only one overload would be valid, but this didn't help.
#include <string>
#include <type_traits>
void foo(int, int){}
void foo(std::string, std::string) {}
template <bool Val1, bool Val2, bool ...Rest>
struct And
{
enum {value = And<Val1 && Val2, Rest...>::value};
};
template <bool Val1, bool Val2>
struct And<Val1, Val2>
{
enum {value = Val1 && Val2};
};
template <typename ...Params, typename ...Args, typename = typename std::enable_if<
And<std::is_convertible<Args, Params>::value...>::value
>::type>
void Invoke(void (*fn)(Params...), Args ...args){}
int main() {
Invoke(&foo, "a", "b");
return 0;
}
Try on ideone.
The compiler complains that mismatched argument pack lengths while expanding ‘std::is_convertible<Args, Params>::value’
, when both overloads are present.
When I comment out the int
overload, the program compiles just fine, and when I comment out the std::string
overload, deduction fails as it should, since const char[]
is not implicitly convertible to int
.
The standard (section 17.8.2.1.6.2 of the C++17 standard) says the following:
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.
So I would expect that the compiler would try the int
overload, where deduction would fail. When it tries the std::string
overload, the deduction would succeed.
Since the deduction would succeed for only one of the overload set members, I would expect that it would then proceed as if the int
overload didn't exist, and the compilation would succeed like it does when the other overload is commented out, but it fails.
Where am I wrong?
References to standard will be appreciated.
typename ...Params, typename ...Args,
seems off. How do you expect the compiler to differentiate between the two? But again, not too familiar with vardic templates, so maybe I'm just missing something. – user10957435template <bool... Args> struct And :std::bool_constant<(Args && ...)> {};
? – L. F.struct And
at all, and just use the fold expression directly – Tomeamis