Lambda Expression in C++



Lambda Expression

A lambda expression in C++11 allows the user to define an anonymous function (a function without any name) inline, which captures variables from the surrounding scope. This makes them a powerful feature for various use cases, like callbacks, sorting, functional programming, etc.

Syntax

Here is the syntax of lambda expression in C++:

[capture](parameters) -> return_type 
{ 
    function_body 
}

Where,

  • capture specifies which variables from the outer scope are captured. It captures variables by value, by reference, or by both methods.
  • parameters are the input parameters for lambda.
  • return_type defines the return type of the lambda function. If the return type needs to be explicitly defined, it will follow this -> symbol.
  • body is the main body of the lambda, where function logic is written.

Example of Lambda Expression

In the following example, a lambda expression is used to add two numbers and return the result:

#include <iostream>
int main() {
    // Define a lambda expression to add two numbers
    auto add = [](int a, int b) {
        return a + b; 
    };
    // Call the lambda expression
    int result = add(5, 3);

    std::cout << "The sum of 5 and 3 is: " << result << std::endl;
    return 0;
}

Output

The sum of 5 and 3 is: 8

Capturing Variables in Lambda Expression

Capturing variables in lambda expressions allows lambda to access variables from its surrounding scope. By a capture clause, a lambda can capture variables from its surrounding scope and allow it to use those variables inside the lambda body.

Types of Variable Capture:

1. Capture by value ([x])

It captures the variables by values, which means lambda gets a copy of the variables and further cannot modify the original variable outside the lambda.

[x](parameters) -> return_type { body }

2. Capture by reference ([&x])

It captures the variables by reference, which means here the lambda can access and modify the original variables.

[&x](parameters) -> return_type { body }

3. Capture Specific Variables ([x, &y])

This allows you to mix capture types in the same lambda. Here, the user can specify which variables to capture by value or reference.

[x, &y](parameters) -> return_type { body }

4. Captures all variables by value ([=])

It captures all variables in the surrounding scope by value.

[=](parameters) -> return_type { body }

5. Captures all variables by reference ([&])

It captures all variables in the surrounding scope by reference.

[&](parameters) -> return_type { body }

Capture this by reference ([this])

It captures this pointer (a reference to the current object) in a lambda expression. It is useful when the user needs to access member variables or functions from within a lambda in a class method.

6. Capture by Mixed Modes

  • [=, &x] ,It captures all variables by value and x variable by reference.
  • [&, x] ,It captures all variables by reference and x by value.
  • [=, this] ,It captures this pointer by value and all other variables by value.

Return Types in Lambda Expressions

In C++, lambda expressions return the value just like regular functions, and its return type can be automatically deduced by the compiler or explicitly specified by the programmer.

1. Automatic Return Type Deduction

In this, the compiler deduces the return type based on the return expression inside the lambda.

a) Implicit Return Type

In this, the return type is based on the return expression, which means users don't need to explicitly specify the return type; it will automatically be inferred from the type of the expression.

[capture](parameters) { return expression; }

b) Returning References

It returns references to variables or values; for this, make sure that the referenced variable stays in scope for the lifetime of the lambda.

[capture](parameters) -> type& { return reference; }

c) Returning Pointers

A lambda can also return a pointer to a variable or dynamically allocated memory.

[capture](parameters) -> type* { return pointer; }

d) Type Deduction with auto

Here, you can also use an auto keyword for the return type, and the compiler will deduce the correct return value type based on an expression.

[capture](parameters) -> auto { return value; }

2. Explicit Return Type

In this, if the user wants to specify lambda's return type explicitly, then use -> return_type syntax. This is useful when working with any complex types and when the return type isn't obvious.

[capture](parameters) -> return_type { return expression; }

Example

#include <iostream>
using namespace std;
int main() {
    int x = 5;
    int y = 10;
    auto my_lambda = [=, &x]() -> int {
        cout << "Inside lambda:" << endl;
        // can't modify 'y' as it's captured by value

        // Modifying 'x' as it's captured by reference
        x += 10;
        cout << "Captured 'x' by reference inside lambda: " << x << endl;
        // Captured 'y' by value, so it can't be modified here
        // simple operation with 'y' and a local value
        int sum = y + 5;
        cout << "Captured 'y' by value inside lambda: " << y << endl;
        cout << "Sum of 'y' and 5 inside lambda: " << sum << endl;
        return sum;
    };
    // Call the lambda
    int result = my_lambda();
    cout << "Result returned from lambda: " << result << endl;
    cout << "Value of 'x' outside lambda after modification: " << x << endl;
    cout << "Value of 'y' outside lambda (no modification): " << y << endl;
    return 0;
}

When the above code is compiled and executed, it produces the following result −

Inside lambda:
Captured 'x' by reference inside lambda: 15
Captured 'y' by value inside lambda: 10
Sum of 'y' and 5 inside lambda: 15
Result returned from lambda: 15
Value of 'x' outside lambda after modification: 15
Value of 'y' outside lambda (no modification): 10

Recursive Lambdas

In C++, recursive lambda is the lambda function that calls itself over again and again during its execution until it reaches its base case. As lambda by default cannot call themselves directly, because they don't have a name. So for this, we can make lambda recursive by using a function pointer or std::function.

Example

#include <iostream>
#include <functional> // for std::function
using namespace std;
int main() {
    // Defining the recursive lambda using std::function
    std::function<int(int)> factorial = [&](int n) -> int {
        if (n <= 1) return 1; // Base case
        return n * factorial(n - 1); // Recursive call
    };
    cout << "Factorial of 5: " << factorial(5) << endl;
}

When the above code is compiled and executed, it produces the following result −

Factorial of 5: 120
Advertisements