Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass lambda expression to lambda argument c++11

Tags:

I would like to do something like this:

int main()
{
    auto f = [/*some variables*/](/*take lambda function*/)
    {/*something with lambda function*/};

    f([/*other variables*/](/*variables to be decided by f()*/)
    {/*something with variables*/});
}

I know that it is possible to pass a lambda to a function, as well as to a lambda. The following works:

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};

    f([](int i) -> double
    {return 0.0;});
}

But the following does not work (as soon as i change the scope variables to add [x])

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;}

    f([x](int i) -> double    //[x] does not work
    {return 0.0;});
}

which gives the error:

error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list
        argument types are: (lambda [](int)->double)
        object type is: lambda [](double (*)(int))->double

would anyone have an idea as to how to fix this, or a way around it? I am using the intel compiler icpc (ICC) 13.1.2 with std=c++11

Thanks

like image 897
Couchy Avatar asked Jul 15 '13 20:07

Couchy


People also ask

What is the correct syntax for lambda expression in C++11?

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

How do you pass lambda expression?

If we need to pass a lambda expression as an argument, the type of parameter receiving the lambda expression argument must be of a functional interface type. In the below example, the lambda expression can be passed in a method which argument's type is "TestInterface".

How do you pass a lambda function in C++?

Permalink. All the alternatives to passing a lambda by value actually capture a lambda's address, be it by const l-value reference, by non-const l-value reference, by universal reference, or by pointer.

What does [&] mean in C++?

It's a lambda capture list and has been defined in C++ from the C++11 standard. [&] It means that you're wanting to access every variable by reference currently in scope within the lambda function.


1 Answers

There are a couple of things to clarify regarding your question. The first of which is what is a lambda?

A lambda expression is a simple expression from which the compiler will generate a unique type that cannot be named, and at the same time it will generate an instance of the type. When you write: [](int i) { std::cout << i; } the compiler will generate a type for you that is roughly:

struct __lambda_unique_name {
   void operator()(int i) const { std::cout << i; }
};

As you can see, it is not a function, but a type that implements operator() as a const member function. If the lambda did any capture, the compiler would generate code to capture the value/references.

As a corner case, for lambdas like the above, where there is no state being captured, the language allows for a conversion from the lambda type to a pointer to function with the signature of the operator() (minus the this part), so the lambda above can be implicitly converted to a pointer to function taking int and returning nothing:

void (*f)(int) = [](int i) { std::cout << i; }

Now that the basics have been stated, in your code you have this lambda:

auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;};

The rules for parameters to functions (that also apply to lambdas) determine that an argument cannot be of type function, so the argument to the lambda decays to a pointer to function (in the same way that an argument of type array decays to a pointer type):

auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};

At a later point you are trying to pass a lambda that has a capture as an argument. Because there is a capture, the special rule does not apply and the lambda is not convertible to a pointer to function yielding the compiler error that you see.

In the current standard you can go one of two ways. You can use type-erasure to remove the exact type of the callable entity from the signature:

auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};

Because a std::function<double(int)> can be initialized with any callable entity with the appropriate signature, this will accept the lambdas in the code below, at the cost of type-erasure that usually implies a dynamic allocation and dynamic dispatch.

Alternatively, you can drop the syntactic sugar and roll the first lambda equivalent manually, but make it generic. In this case, where the lambda is simple this could be a valid option:

struct mylambda {
   template <typename F>
   double operator()(F fn) const {
      fn(0); return 0.0;
   }
} f;
// then use the non-lambda as you tried:
f([x](int i) -> double {return 0.0;});

Finally, if you are patient enough, you can wait for C++14, where (most probably, it has not yet been ratified) there will be support for polymorphic lambdas which simplify the creation of the above class:

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above
like image 120
David Rodríguez - dribeas Avatar answered Oct 26 '22 08:10

David Rodríguez - dribeas