Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ lambda expressions - How does the compiler interpret them?

Tags:

c++

c++11

lambda

I just starter learning the new features in C++ 11. I was reading about lambdas in C++ Primer (Stanley Lippman) and was experimenting with them.

I tried the following pieces of code:

auto func() -> int (*) (){
    //int c=0;
    return []()-> int {return 0;};
}

int main(){
    auto p = func();
}

This code compiled fine. So I guess lambdas without any captures are just generated as normal functions by the compiler and we can use a normal function pointer to them.

Now I changed the code to use captures:

auto func() -> int (*) (){
    int c=0;
    return [=]()-> int {return c;};
}

int main(){
    auto p = func();
}

But this failed to compile. I got the following compilation error while using g++:

main.cpp: In function ‘int (* func())()’:
main.cpp:6:31: error: cannot convert ‘func()::__lambda0’ to ‘int (*)()’ in return
return [=]()-> int {return c;};

From the error I can understand that it is not a normal function that is generated and it might probably be a class with an overloaded call-operator. Or is it something else?

My questions : How does the compiler handle lambdas internally? How should I pass around lambdas that use captures i.e. what should be the return value from func()? I cant currently think of a use case where I would need to use lambdas like this but I just want to understand more about them. Please help.

Thanks.

like image 817
MS Srikkanth Avatar asked Apr 06 '14 12:04

MS Srikkanth


2 Answers

All lambdas are function objects with implementation defined type called closure type with a operator() member. Every lambda expression has it's own unique closure type, too.

Lambdas without a capture can be converted to a function pointer. Whether or not compiler generates a normal function behind the scenes is an internal detail and shouldn't matter to you.

It's not possible to return a lambda that's defined inside a function. There are few things that prevent this - you don't know the name of a type of lambda expression, you can't use a lambda expression inside a decltype and as already said, two lambda expressions (even if lexically identical) have different types.

What you can do is use std::function:

std::function<int()> func()
{
    int i = 0;
    return [=]()-> int {return i;};
}

This way works with captures, too.

Or something like this:

auto f = []{ return 0; };

auto func() -> decltype(f)
{
    return f;
}

EDIT: The upcoming C++1y standard (more specifically, return type deduction), however, will allow you to do this:

auto func()
{
    int i = 42;
    return [=]{ return i; };   
}
like image 117
jrok Avatar answered Oct 11 '22 05:10

jrok


A function cannot return a lambda function itself. If you write:

auto func() -> int () {
    int c=0;
    return [=]()-> int {return c;};
}

Then GCC complains:

lambda.cpp:3:21: error: function return type cannot be function
 auto func() -> int (){

So the solution is to wrap the return object into a std::function:

#include <functional>
    auto func() -> std::function<int ()> {
    int c=0;
    return [=]()-> int {return c;};
}

int main(){
    auto p = func();
}
like image 31
hivert Avatar answered Oct 11 '22 04:10

hivert