template<typename ReturnT, typename... ParamT>
void foo(std::function<ReturnT(ParamT...)> callback)
{}
template<typename ReturnT, typename ParamT>
void bar(std::function<ReturnT(ParamT)> callback)
{}
main()
{
foo<int, int>([](int x){ return x; }); // no instance of function
// template matches argument list
bar<int, int>([](int x){ return x; }); // OK
}
The only difference between foo and bar is that foo has variadic arguments. Somehow the compiler is able to convert the lambda to a std::function in bar.
To my understanding, template type deduction doesn't consider type conversions. So shouldn't both fail?
You don't have any deduction for the type parameters of
bar
, they are fully specified.You still have the tail of the pack to deduce in
foo
, and that fails because the lambda isn't astd::function
.now,
foo<int,int>
isfoo<ReturnT=int, ParamsT starts with {int}>
.It does not fully specify
ParamT
. In fact, there is no way to fully specifyParamT
.As an incompletely specified template, deduction occurs, and fails. It doesn't try "what if I just assume the pack doesn't go any further".
You can fix this with:
where
block_deduction
looks like:now deduction is blocked on
foo
's first argument.And your code works.
Of course, if you pass in a
std::function
it will no longer auto-deduce arguments.Note that deducing the type of a a type erasure type like
std::function
is usually code smell.Replace both with:
if you must get arguments, use function traits helpers (there are many on SO). If you just need return value, there are
std
traits that already work that out.In c++17 you can do this:
using the c++17 deduction guides feature.