Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to deduce return type from lambda?

here is the sample code

#include <iostream>

template<typename T>
T foo(T(*fp)())
{
    return fp();
}


int main()
{
    std::cout<<foo([]->int{ return 1; });
}

when I compiled the code above, compiler says it cannot deduce the template argument, but I have specified the return type of lambda.

like image 687
Kwan Avatar asked Dec 30 '22 13:12

Kwan


2 Answers

foo takes function pointer while you're passing a lambda, but implicit conversion (from lambda to function pointer) won't be considered in template argument deduction for T, which makes the invocation failing.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

You can specify template argument explicitly:

foo<int>([]()->int{ return 1; });
// ^^^^^

Or convert lambda to function pointer by operator+ or static_cast.

foo(+[]()->int{ return 1; });
//  ^
foo(static_cast<int(*)()>([]()->int{ return 1; }));
//  ^^^^^^^^^^^^^^^^^^^^^^                    ^

BTW: Omitted parameter list used with trailing return type is a C++23 lambda feature. (So I added ()s.) And as @max66 suggested without trailing return type the lambda works fine too; the return type would be deduced automatically, such as foo(+[]{ return 1; });.

like image 149
songyuanyao Avatar answered Jan 03 '23 03:01

songyuanyao


If you don't want to change the call site, you can always just change foo so it can take a lambda:

template<typename F>
std::invoke_result_t<F> foo(F&& f)
{
    return f();
}

Note also that your lambda currently works only with C++23 - add the (empty) parameter list if you want this to work everywhere:

[]()->int{ return 1; }
like image 28
Useless Avatar answered Jan 03 '23 04:01

Useless