Powerful Stateless Lambda Expressions in C++

Previously, we discussed function pointers and function objects. Now, let’s take a look at the powerful lambda expressions. We will explore its functionalities step by step. First, the most basic lambda expression provides a way to define unnamed functions (anonymous functions). Let’s see how it is used.

1.No parameters and no specified return type

Powerful Stateless Lambda Expressions in C++

The compilation and execution result of the code is:

Powerful Stateless Lambda Expressions in C++

2.With parameters and no specified return type

Powerful Stateless Lambda Expressions in C++

The execution result is:

Powerful Stateless Lambda Expressions in C++

When the return type of the lambda expression is not specified, the compiler deduces the actual return type based on the return statements in the function body. It requires that all return statements return values of the same type, and the compiler does not perform implicit conversions. If conversion is needed, it must be explicitly added. For example, let’s modify the code to make the lambda return different types.

Powerful Stateless Lambda Expressions in C++

As shown in the figure above, the compilation directly reports an error, indicating that the return type bool must match the previous return type int. Let’s add a forced type conversion and see.

Powerful Stateless Lambda Expressions in C++

As shown in the figure above, by using a forced type conversion, the return types are consistent, and the code can compile and run.

3.No parameters and specified return type

This is done by using the -> operator followed by the return type to specify the return type of the lambda. The code and execution result are as follows:

Powerful Stateless Lambda Expressions in C++

Since the return type is specified, even though there are no parameters, the () cannot be omitted as in the previous case where no return type was specified; the -> must follow the ()

4.With parameters and specified return type

Since the return type is specified, the compiler can perform implicit conversions for the return statements. Therefore, the code that previously reported an error due to inconsistent return types can compile and run normally, as shown below:

Powerful Stateless Lambda Expressions in C++

Moreover, starting from C++14, lambda expressions behave like regular functions, and their parameter lists also support default parameters, as shown below:

Powerful Stateless Lambda Expressions in C++

Then, the lambda expression essentially defines a complete function object, which can contain any number of member variables. Unlike the previously mentioned function objects, it no longer requires an explicit definition of the object’s type; the compiler will automatically generate that type. Let’s write a simple code to see.

Powerful Stateless Lambda Expressions in C++

As shown in the figure above, since the actual type is automatically generated by the compiler, we cannot know it in advance and can only use auto to define the variable, allowing it to deduce automatically. If we need to obtain the type, we can use the decltype keyword. The code is as follows:

Powerful Stateless Lambda Expressions in C++

However, for such stateless lambdas, before C++20, it was not possible to use decltype to define variables, so the code reported an error. After C++20, it is supported, and the compilation and execution results are as follows:

Powerful Stateless Lambda Expressions in C++

As for why lambda is a function object and not a function pointer, we can generate a temporary file using the following compilation options.

Powerful Stateless Lambda Expressions in C++

Then, by opening the gimple suffix file, we can see that it overloads the operator() function call operator. As we mentioned earlier, function objects primarily achieve this by overloading the operator().

Powerful Stateless Lambda Expressions in C++

Since lambda is a function object, in the previous article about function objects, we mentioned that when calling functions with function objects or function pointers as parameters, since they are of different types, templates must be used for generalization. In the previous example code, since the type of the lambda was unknown in advance, template parameters were used. Can we write code to see if the calling function can accept a function pointer as a parameter? As follows:

Powerful Stateless Lambda Expressions in C++

Surprisingly, the compilation and execution went smoothly. Since we have already established that it is actually a function object, why did it compile successfully? This is because the compiler guarantees that a lambda closure that does not capture any variables will always have a type conversion operator that can convert it to an equivalent function pointer type. Therefore, in function call parameter passing, it has already been implicitly converted to a function pointer. Since this is the case, let’s manually perform the conversion and see.

Powerful Stateless Lambda Expressions in C++

The execution result from the figure shows that this conversion is fine. However, function objects do not support this conversion by default, so the following will report an error.

Powerful Stateless Lambda Expressions in C++

If we want it to support conversion like lambda, we need to provide a conversion operation similar to how the compiler generates anonymous classes for lambda. As shown below:

Powerful Stateless Lambda Expressions in C++

Powerful Stateless Lambda Expressions in C++

As shown in the two examples above, both can be converted, and there are other conversion methods. How to implement them depends on your own needs.

In summary, we have introduced stateless lambda expressions, which do not capture variables and cannot access any content in their enclosing scope. Next time, we will discuss how to implement variable capture to access variables stored in the defined scope. That’s all for today; next time we will specifically talk about how to capture variables.

Leave a Comment