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);
te
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.
I do not know enough of the standard to say whether this is my compilers fault not implementing it properly or if it's actually the standard, but with VS2015 you cannot generate a compile-time constant lambda expression. And templates only take compile time constants, so no lambdas.
However, you don't need it to be a template if you want to pass a lambda. It's perfectly possible without:
#include <functional>
int function(std::function<int(int)> const& f, int a)
{
f(a);
}
int test(int a)
{
return a;
}
int main()
{
auto lambda = [](int a) -> int { return a; };
function(test, 1);
function(lambda, 1);
return 0;
}
This is only because "You cannot use the name or address of a local variable as a template argument.". If you want to make a global static lambda, go refer to http://pfultz2.com/blog/2014/09/02/static-lambda/ you may find what you want.
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);
}