I'm having a hard time figuring out what causes the substitution failure in this example code:
bool f(int a, int b, float c)
{
printf("%d %d %f", a, b, c);
return true;
}
template <typename ...Params>
void call1(Params... params, std::function<bool(Params...)> func)
{
func(params...);
}
template <typename ...Params>
void call2(std::function<bool(Params...)> func)
{
}
Somewhere in main:
call1<int, int, float>(3, 4, 5.5, f); // Ok.
call2<int, int, float>(f); // Substitution failure.
The compiler says:
template argument deduction/substitution failed: mismatched types 'std::function<bool(Params ...)>' and 'bool (*)(int, int, float)'
call2<int, int, float>(f);
^
What baffles me is that call1 works while call2 doesn't. Any tips? =)
First: You can specify less arguments than what you use and let the compiler deduce the rest:
template <typename ...Params>
void func1(Params... params);
func1<int, int>(1, 2, 3); // calls func1<int, int, int>
This means Params
is still open for adding extra types when you call it. But if you take the function address, it becomes defined and closed:
auto x = func1<int, int>;
x(1, 2, 3); // not possible
When you call your call1
function directly:
template <typename... Params>
void call1(Params... params, std::function<bool(Params...)> func);
call1<int, int, int>(1, 2, 3, f);
call1<int>(1, 2, 3, f); // same as before
call1(1, 2, 3, f); // same as before
The compiler is able to deduce that you have exactly 3 ints because you just sent him 3 ints. This way the last parameter must be std::function<bool(int, int, int)>
because we fully deduced what Params...
means and there's no space for more types.
Now the problematic case:
template <typename... Params>
void call2(std::function<bool(Params...)> func);
call2<int, int, int>(f);
Here you informed the compiler that the first 3 elements of Params
are all ints. Params = {int, int, int, ...}
. Note that it is still open for adding something else if deduction tells so. Replacing we have: std::function<bool(int, int, int, ...)> func
. The compiler can't possibly know what this incomplete type means unless you explicitly pass a std:function
(exact match). It doesn't know yet it can have a constructor taking the function pointer you provided, so there's a mismatch. Now the compiler does not have enough data to decide if it need more types into Params
or not. Failure.
But note this interesting case:
auto x = call2<int, int, int>;
x(f); // x is exactly void(*)(std::function<bool(int, int, int)>). No doubts.
Here the you force Params
to be complete. There's no deduction to evaluate. Although ugly, this also works:
(&call2<int, int, int>)(f);
The compiler can't deduce the type with your current code (even though the function pointer is implicitly convertible to a std::function
). You could create a traits class to help deduce the correct type.
template <typename... Params>
struct func_traits {
using func_type = std::function<bool(Params...)>;
};
template <typename ...Params>
void call2(typename func_traits<Params...>::func_type func) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With