c++std-functiontemplate-argument-deductionfunction-templates

How to expand function template parameters package in std::function as the function's argument?


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?


Solution

  • 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
    

    Demo

    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}}: