what's the type of a lambda expression ?

I have written some code using template classes that take a function or functor type as a template parameter and functor or function as a constructor parameter. Now, I am turning my code into a library and am struggling with the declaration of the type of some of the objects in the .h file; even with the help of decltype(), it is challenging because the expressions needed as an argument are rather convoluted.

Most of the difficulty comes from the use of lambda expression. The attached code reproduces the context.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

template<typename T, typename F>
class funner{
    F fun;
    T val;
    public:
    funner(T v, F f):fun(f),val(v){}
    auto eval(){return fun(val);}
};

template<typename T, typename F>
funner<T,F> make_funner(T val,F foo){
    return funner<T,F>(val, foo);
}


int main(){

double fact = 9.89;
auto f = [fact](int x){return double(x)*fact;};
auto x = make_funner(10,f);
std::cout << x.eval() << std::endl;
//int k = f; //cannot convert ‘main()::<lambda(int)>’ to ‘int’ 

}


So I wonder: how to express the type of a lambda expression?

I purposely created a compiler error at line 24 to peek at what it had to say about the type of variable f. I was surprised to see it described as
main()::<lambda(int)

- What does it mean?
- Why isn't it just something (a functor ?) that takes an int and returns a double?
- Why is there no mention of the return type
- Why is there a reference to "main()"
- I was not expecting a reference to the closure ([fact]) and there is not
Last edited on
Each lambda expression has its own unique type. There is no way to refer to this type by name. Don't let the name that you see in the error message fool you. Even if two lambdas appear to have the same name they are still different types.

1
2
3
4
5
6
7
auto f1 = [](){};
auto f2 = [](){};

std::cout << "f1 : " << typeid(f1).name() << '\n';
std::cout << "f2 : " << typeid(f1).name() << '\n';

std::cout << "same? " << (std::is_same_v<decltype(f1) ,decltype(f2)> ? "yes" : "no") << '\n';
Possible output:
f1 : Z4mainEUlvE_
f2 : Z4mainEUlvE_
same? no


If you need the lambdas to have a common type you can wrap them up into std::functions, which can even be used on functions and other callable things, not just lambdas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <functional>

double fun(int) { return 1.8; }

struct Functor
{
	double operator()(int){ return 5.0; }
};

int main()
{
	double fact = 9.89;
	std::function<double(int)> f = [fact](int x){return double(x)*fact;};
	std::function<double(int)> g = fun;
	std::function<double(int)> h = Functor();
	
	// f, g and h has the same type so they can be assigned 
	// and passed around just like any other value.
	
	for (auto callable : {f, g, h})
	{
		std::cout << callable(1) << '\n';
	}
}
9.89
1.8
5

https://en.cppreference.com/w/cpp/utility/functional/function
Last edited on
Put another way...

The type of a lambda is implementation defined, meaning that from C++ it doesn’t actually have a type.

As Peter87 said, if you need to pass a lambda value around, use std::function, which has a very definite type — one that is actually useful to you in code and template introspections.
Thank you very much.

With std::function, I was indeed able to define a type for all the lambdas which in turn allowed me to define the type of the classes wrapping those lambdas (a bunch of lazy evaluators). Now that I have an explicit type, I can finally move the method definitions to the .cpp file where they belong and use the explicit type definition in the declaration file.

Et voila!
Topic archived. No new replies allowed.