I have a template that accepts a function as an argument.
When I try to pass a lambda expression it does not compile.
typedef int (*func)(int a);
template <func foo>
int function(int a)
{
foo(a);
}
int test(int a)
{
return a;
}
int main()
{
function<test>(1); // ---> this is ok
auto lambda = [](int a) -> int { return a; };
function<lambda>(1); // ---> this is wrong, why?
return 0;
}
What I am missing?
Passing Lambda Expressions as Arguments If you are passing an instance of a class as a parameter, you must specify the class name or the object class as a parameter to hold the object. In Java, there is no type for lambda expression.
From the various lambda improvements, template parameters for lambdas are my favorite ones. Lambdas support with C++20 template parameters, can be default-constructed and support copy-assignment, when they have no state, and can be used in unevaluated contexts.
Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.
Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };
A lambda is not a function pointer! A lambda is an instance of compiler generated class!
However, a non capturing lambda may be converted to a function pointer using it's operator+
Here's an example:
int main() {
auto lambda = [](int a) { return a; };
func ptr = +lambda; // this would work
return 0;
}
Sadly, the operator+
won't even work in your case because it has not been declared as constexpr, so you can't use it in a template parameter.
A fix to your case would be to use a free function... until N4487 is not accepted, you can't expect to pass lambda as template parameter.
Another fix would be to create your own functor instead of a lambda:
struct LambdaType {
constexpr LambdaType() = default;
int operator()(int a) {
return run(a);
}
// this is a non-capturing lambda, the operator can be
// in a static function
static int run(int a) {
return a;
}
};
int main() {
LambdaType lambda;
function<&LambdaType::run>(1); // ---> this is working
return 0;
}
This solution is not quite appealing, but it might be useful if LambdaType
is hidden in a cpp file.
If your goal is only the compiler to be able to inline your code, you can use templates to pass the lambda around:
#include <iostream>
template <typename T>
int function(T foo, int a) {
return foo(a);
}
int main() {
int a;
std::cin >> a;
int b = function([](int a) { return a; }, a);
return b;
}
Since the compiler knows the type of T
for each instanciation, a good compiler should be able to optimize out the lambda.
With clang, the third option gives the following assembly:
main: # @main
pushq %rax
leaq 4(%rsp), %rsi
movl std::cin, %edi
callq std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
movl 4(%rsp), %eax # this is the call to the function
addq $8, %rsp
retq
pushq %rax
movl std::__ioinit, %edi
callq std::ios_base::Init::Init()
movl std::ios_base::Init::~Init(), %edi
movl std::__ioinit, %esi
movl $__dso_handle, %edx
popq %rax
jmp __cxa_atexit # TAILCALL
I used -std=c++14 -Ofast -march=native
as flags.
This is because the lambda as its own type.
You have templatize function()
on the type of the function passed.
template<typename F>
int function(F foo, int a) {
return foo(a);
}
int test(int a) {
return a;
}
int main()
{
// function will work out the template types
// based on the parameters.
function(test, 1);
function([](int a) -> int { return a; }, 1);
}
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