In the C++ standard, closure types are defined as follows:
[expr.prim.lambda.closure] The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below. [...]
The standard does not seem to define whether or not the unnamed non-union class type is final. Would a compiler implementing lambdas as final classes be standard compliant, or do we have the guarantee that one can inherit from lambdas?
The question is not whether it is useful or not to inherit from lambdas: it's a given that it is useful. The question is whether the standard provides this guarantee.
50: Use a lambda when a function won't do (to capture local variables, or to write a local function) The difference in the usage of functions and lambda functions boils down to two points. You can not overload lambdas. A lambda function can capture local variables.
C++ Lambda expression allows us to define anonymous function objects (functors) which can either be used inline or passed as an argument. Lambda expression was introduced in C++11 for creating anonymous functors in a more convenient and concise way.
In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it's invoked or passed as an argument to a function.
Yes, the closure type must not be final. At least that's my interpretation.
§8.1.5.1 Closure types [expr.prim.lambda.closure]
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
- ... [does not apply]
The standard then doesn't describe the closure type as being final. Making it final would alter the observable behavior ergo the closure type must not be final.
Regarding the observable behavior. Consider this:
auto l = []{};
return std::is_final_v<decltype(l)>;
Making the closure type final would clearly modify the observable behavior of a valid program.
As to a use case, it can actually be a very useful feature:
template <class... Fs> struct Overload : Fs ...
{
using Fs::operator()...;
};
template <class... Fs> Overload(Fs...) -> Overload<Fs...>;
auto test()
{
Overload f = {[] (int a) { return a * 100; },
[] (int a, int b) { return a + b;}};
return f(1) + f(2, 3); // 105
}
See it in action on godbolt
Thanks to hvd and rakete1111 for the discussions and feedback in the comments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With