Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I instantiate a lambda closure type in C++11/14?

I'm aware that there's no default constructor for lambda closure type. But does that mean it's impossible to instantiate it after it being passed as a template parameter?

Consider the following minimal example:

#include <iostream>

template <typename FuncType>
std::pair<int,int> DoSomething() {
    return FuncType()(std::make_pair(1,1));
}

int main() {
    auto myLambda = [](std::pair<int,int> x) {
        return std::make_pair(x.first*2,x.second*2);
    };
    std::pair<int,int> res = DoSomething<decltype(myLambda)>();
    return 0;
}

For performance reasons, I can't use std::function to avoid virtual pointer calls. Is there a way to do this? I need to instantiate that lambda once and use it many times inside that function.

How does the standard library make it work when decltype(myLambda) is passed to something like std::map comparators in the template parameter?

like image 336
The Quantum Physicist Avatar asked Jun 01 '19 13:06

The Quantum Physicist


People also ask

What is a closure in lambdas?

Closures and the Lambdas 1 , the capability that allows a method or a function to reference a non-local variable or value is called closure. 2 Non-local Variables. In C#, accessing a variable outside the scope of a function is possible and considered normal. ... 3 Lambda Functions. ... 4 Closures. ... 5 Conclusion. ...

What is a Lambdas in C++?

Lambdas, also known as closures, are unnamed function objects in C++. Unnamed means we cannot know or name their type . Because their type is unknown to us lambdas can only ever be stored in an auto variable on the stack.

What does it mean when a Lambdas is unnamed?

Unnamed means we cannot know or name their type . Because their type is unknown to us lambdas can only ever be stored in an auto variable on the stack. Function means lambdas implement the function call operator ( ()) and object means lambdas can store data, just like a struct or class.

How do you use a lambda function with an empty capture?

Under the final C++11 spec, if you have a lambda with an empty capture specification, then it can be treated like a regular function and assigned to a function pointer. Here's an example of using a function pointer with a capture-less lambda: typedef int (*func) (); func f = [] () -> int { return 2; }; f ();


2 Answers

Although this feature is coming in C++20 (see songyuanyao's answer), you don't actually need that in this case. You can just pass the lambda as a function parameter of type FuncType and call that multiple times:

template <typename FuncType>
std::pair<int,int> DoSomething(FuncType f)
{
    return f(std::make_pair(1,1));
}

int main()
{
    auto myLambda = [](std::pair<int,int> x) {
        return std::make_pair(x.first*2,x.second*2);
    };
    std::pair<int,int> res = DoSomething(myLambda);
}
like image 184
Nikos C. Avatar answered Oct 24 '22 15:10

Nikos C.


I'm aware that there's no default constructor for lambda closure type.

Yes, this is true until C++20. (Note that since C++20 if no captures are specified, the closure type has a defaulted default constructor.)

Closure types are not DefaultConstructible. Closure types have a deleted (until C++14) no (since C++14) default constructor. (until C++20)

And

How does the standard library make it work when decltype(myLambda) is passed to something like std::map comparators in the template parameter?

There's nothing special for the standard library. If you specify a non-DefaultConstructible lambda as the comparator type for std::map, you have to pass an object to the constructor, std::map will initialized its comparator via copy; lambda has copy and move constructor.

You can change your code to the same way as std::map's constructor:

template <typename FuncType>
std::pair<int,int> DoSomething(const FuncType& f = FuncType()) {
    // auto z(f);   // you can take a copy if necessary
    return f(std::make_pair(1,1));
}

then

auto myLambda = [](std::pair<int,int> x) {
    return std::make_pair(x.first*2,x.second*2);
};
std::pair<int,int> res = DoSomething<decltype(myLambda)>(myLambda);

LIVE

like image 29
songyuanyao Avatar answered Oct 24 '22 17:10

songyuanyao