This question is an extension of another question from a decade ago:
#include<functional>
template <typename ReturnType, typename... ArgumentTypes>
struct Caller
{
static void Call(std::function<ReturnType(ArgumentTypes...)>);
};
template <typename ReturnType, typename...ArgumentTypes>
void Call(std::function<ReturnType(ArgumentTypes...)>f);
int main()
{
Caller<void>::Call([]() {});//Well-formed
Call<void>({[]() {}});//Well-formed
Call<void>([]() {});//Ill-formed
}
The code provides three methods to expand the template parameter package in the std::function
signature. The first two have been tested as well-formed and can be compiled; the last is the same as the old question and is thus ill-formed. The question is, in what way do the first two methods bypass the problem that causes compilation failure in the original problem and the last method?
For struct Caller
, all template arguments are passed (just void
and empty pack) as there are no partial CTAD. Lambda can be converted to std::function<void()>
, so it is ok.
For function template Call
, provided argument are just the first ones, deduction might still happens.
One way to stop possible deduction is to store in intermediate variable:
auto call = Call<void>; // void (*)()
call([](){}); // OK
lambda are not std::function
so cannot be deduced as std::function<Ret(Args...)>
, so third method fails.
For the second, the extra {..}
also stops deduction:
See template_argument_deduction#Non-deduced_contexts #7 for more details:
The parameter P, whose A is a braced-init-list, but P is not
std::initializer_list
, a reference to one (possibly cv-qualified), or a reference to an array}}: