Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Creating a Lambda Expression in C++:
3.
Example
3.1.
C++
4.
C++ Lambda Function With Parameters
4.1.
Example
4.2.
C++
5.
C++ Lambda Function With Return Type
5.1.
Example 
5.2.
C++
6.
Example 2: C++ Lambda - Explicit Return Type
6.1.
C++
7.
C++ Lambda Function Capture Clause
7.1.
Capture by Value
7.2.
Capture by Reference
8.
Example 3: C++ Lambda Capture by Value:
8.1.
C++
9.
Example 4: C++ Lambda Capture by Reference:
9.1.
C++
10.
Example: C++ Lambda Function as Argument in STL Algorithm:
10.1.
C++
11.
Frequently Asked Questions
11.1.
What are lambda expressions used for in C++?
11.2.
Can a lambda expression capture any kind of variable?
11.3.
How does capturing by reference differ from capturing by value in lambda expressions?
12.
Conclusion 
Last Updated: Jun 11, 2024
Easy

Lambda Expression in C++

Author Rahul Singh
0 upvote
Leveraging ChatGPT - GenAI as a Microsoft Data Expert
Speaker
Prerita Agarwal
Data Specialist @
23 Jul, 2024 @ 01:30 PM

Introduction

Lambda expressions are a powerful feature introduced in C++11 that allow you to define anonymous functions inline. They provide a concise and simple way to write small, one-time-use functions without the need to define a separate named function. Lambda expressions are particularly useful when you need to pass a function as an argument to another function or when you want to cover a small piece of code. 

Lambda Expression in C++

In this article, we will learn about the syntax and usage of lambda expressions in C++, which include how to create lambda functions, pass parameters, specify return types, and capture variables from the surrounding scope. 

Creating a Lambda Expression in C++:

In C++, a lambda expression is defined using the following syntax:

[capture clause] (parameters) -> return-type { function body }


The capture clause allows you to specify which variables from the surrounding scope should be captured by the lambda function. The parameters are optional and represent the arguments passed to the lambda function. The return type is also optional and can be deduced by the compiler based on the function body. The function body contains the code that will be executed when the lambda function is invoked.

Let's understand each part of the lambda expression syntax:

  1. Capture Clause: It specifies how variables from the enclosing scope are captured by the lambda function. You can capture variables by value or by reference.
     
  2. Parameters: They define the arguments that the lambda function accepts. The parameter list is enclosed in parentheses and follows the same syntax as regular function parameters.
     
  3. Return Type: It specifies the type of the value returned by the lambda function. If the return type is omitted, the compiler will deduce it based on the function body. You can explicitly specify the return type using the -> operator followed by the type.
     
  4. Function Body: It contains the code that will be executed when the lambda function is called. The function body is enclosed in curly braces {}.
Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

Example

Consider a scenario where you have a vector of integers and you want to count the number of even numbers in the vector.

  • C++

C++

#include <iostream>

#include <vector>

#include <algorithm>

int main() {

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int evenCount = std::count_if(numbers.begin(), numbers.end(), [](int num) {

   return num % 2 == 0;

});

std::cout << "Number of even numbers: " << evenCount << std::endl;

return 0;

}


In this example:

  1. We include the necessary headers: <iostream> for input/output, <vector> for the vector container, & <algorithm> for the count_if function.
     
  2. We create a vector called numbers & initialize it with some integer values.
     
  3. We use the std::count_if function from the <algorithm> header to count the number of even numbers in the vector. The count_if function takes three arguments:
     
  • The beginning iterator of the range to search (numbers.begin())
     
  • The end iterator of the range (numbers.end())
     
  • A predicate function that returns true for the elements to be counted
     
  1. We pass a lambda function as the predicate to count_if. The lambda function takes an integer parameter num & returns true if num is even (i.e., num % 2 == 0).
     
  2. The count_if function returns the count of elements for which the lambda function returns true, which in this case is the count of even numbers.
     
  3. Finally, we print the count of even numbers using std::cout.
     

Output:

Number of even numbers: 5


The lambda function [](int num) { return num % 2 == 0; } is defined inline as an argument to count_if. It captures nothing (empty capture clause), takes an integer parameter num, & returns true if num is even.

C++ Lambda Function With Parameters

Lambda functions in C++ can accept parameters, allowing you to pass arguments to the function when it is invoked. The parameters are specified within the parentheses () of the lambda expression.

Example

  • C++

C++

#include <iostream>

#include <vector>

#include <algorithm>

int main() {

std::vector<int> numbers = {1, 2, 3, 4, 5};

int threshold = 3;

int count = std::count_if(numbers.begin(), numbers.end(), [threshold](int num) {

   return num > threshold;

});

std::cout << "Count of numbers greater than " << threshold << ": " << count << std::endl;

return 0;

}


In this example:

We have a vector of integers called numbers.
 

  1. We define an integer variable threshold with a value of 3.
     
  2. We use the std::count_if function to count the number of elements in numbers that are greater than threshold.
     
  3. The lambda function [threshold](int num) { return num > threshold; } takes an integer parameter num and captures threshold by value.
     
  4. Inside the lambda function, we compare each element num with the captured threshold value and return true if num is greater than threshold.
     
  5. The count_if function returns the count of elements for which the lambda function returns true.
     
  6. Finally, we print the count of numbers greater than the threshold.
     

Output:

Count of numbers greater than 3: 2


In this example, the lambda function captures threshold by value, so it can access the value of threshold from the surrounding scope. The lambda function takes an integer parameter num, which represents each element of the numbers vector during the count_if operation.

C++ Lambda Function With Return Type

In C++, you can specify the return type of a lambda function explicitly using the arrow notation -> followed by the return type. If the return type is not specified, the compiler will deduce it based on the function body.

Example 

  • C++

C++

#include <iostream>

#include <vector>

#include <algorithm>

int main() {

std::vector<int> numbers = {1, 2, 3, 4, 5};

int sum = std::accumulate(numbers.begin(), numbers.end(), 0, [](int a, int b) -> int {

   return a + b;

});

std::cout << "Sum of numbers: " << sum << std::endl;

return 0;

}

In this example:

  1. We have a vector of integers called numbers.
     
  2. We use the std::accumulate function from the <algorithm> header to calculate the sum of all elements in the numbers vector.
     
  3. The accumulate function takes four arguments:
     
  • The beginning iterator of the range (numbers.begin())
     
  • The end iterator of the range (numbers.end())
     
  • The initial value of the accumulation (0 in this case)
     
  • A binary operation function that defines how the elements are accumulated
     
  1. We pass a lambda function as the binary operation function to accumulate. The lambda function takes two integer parameters a and b and explicitly specifies the return type as int using the arrow notation -> int.
     
  2. Inside the lambda function, we return the sum of a and b, which represents the accumulated value and the current element, respectively.
     
  3. The accumulate function returns the final accumulated sum of all elements.
     
  4. Finally, we print the sum of numbers.
     

Output:

Sum of numbers: 15

In this example, the lambda function [](int a, int b) -> int { return a + b; } explicitly specifies the return type as int using the arrow notation. This is useful when you want to make the return type clear and avoid any ambiguity.

Note : However, in most cases, the compiler can deduce the return type automatically based on the function body, so explicitly specifying the return type is often optional.

Example 2: C++ Lambda - Explicit Return Type

Let's look at another example that demonstrates the usage of lambda functions with explicit return types:

  • C++

C++

#include <iostream>

#include <vector>

#include <algorithm>

int main() {

std::vector<int> numbers = {1, 2, 3, 4, 5};

auto square = [](int num) -> int {

   return num * num;

};

std::vector<int> squaredNumbers;

std::transform(numbers.begin(), numbers.end(), std::back_inserter(squaredNumbers), square);

for (int num : squaredNumbers) {

   std::cout << num << " ";

}

std::cout << std::endl;

return 0;

}


In this example:

  1. We have a vector of integers called numbers.
     
  2. We define a lambda function square that takes an integer parameter num and explicitly specifies the return type as int using the arrow notation -> int.
     
  3. Inside the lambda function, we calculate the square of num by multiplying it with itself and return the result.
     
  4. We create an empty vector called squaredNumbers to store the squared values.
     
  5. We use the std::transform function from the <algorithm> header to apply the square lambda function to each element of the numbers vector and store the results in squaredNumbers.

 

The transform function takes four arguments:

  • The beginning iterator of the input range (numbers.begin())
     
  • The end iterator of the input range (numbers.end())
     
  • The iterator to the beginning of the output range (std::back_inserter(squaredNumbers))
     
  • The unary operation function to apply to each element (square)
     
  1. The std::back_inserter is an iterator adaptor that allows us to insert elements at the end of the squaredNumbers vector using the push_back operation.
     
  2. Finally, we use a range-based for loop to print the squared numbers.
     

Output

1 4 9 16 25


In this example, the lambda function square explicitly specifies the return type as int. The transform function applies the square lambda function to each element of the numbers vector and stores the squared values in the squaredNumbers vector.

Note : By using lambda functions with explicit return types, you can create reusable and self-contained functions that can be passed as arguments to algorithms or stored in variables.

C++ Lambda Function Capture Clause

The capture clause in a lambda function allows you to capture variables from the surrounding scope and use them within the lambda function. There are two ways to capture variables:

  1. Capture by Value: Capturing variables by value means that a copy of the variable is made and stored within the lambda function. Any modifications to the captured variable inside the lambda function do not affect the original variable.
     
  2. Capture by Reference: Capturing variables by reference means that the lambda function directly refers to the original variable. Any modifications to the captured variable inside the lambda function will affect the original variable.
     

The capture clause is specified within the square brackets [] of the lambda expression. You can capture variables individually or use capture defaults.

Let's discuss each capture method in more detail:

Capture by Value

To capture variables by value, you simply list the variables you want to capture within the square brackets, separated by commas. 

For example

int x = 10;
int y = 20;
auto lambda = [x, y]() {
    // x and y are captured by value
    // ...
};

In this case, the variables x and y are captured by value. The lambda function will have its own copies of x and y, and any modifications to these variables inside the lambda function will not affect the original variables.

Capture by Reference

To capture variables by reference, you prefix the variable names with an ampersand & within the square brackets.

For example

int x = 10;
int y = 20;
auto lambda = [&x, &y]() {
    // x and y are captured by reference
    // ...
};

In this case, the variables x and y are captured by reference. The lambda function will refer to the original variables, and any modifications to these variables inside the lambda function will affect the original variables.

You can also use capture defaults to simplify the capture clause:

  • [=] captures all variables used in the lambda function by value.
     
  • [&] captures all variables used in the lambda function by reference.
     

For example:

int x = 10;
int y = 20;
auto lambda = [=]() {
    // x and y are captured by value
    // ...
};


In this case, all variables used within the lambda function, including x and y, are captured by value using the [=] capture default.

Note : It's important to choose the appropriate capture method based on your requirements. Capturing by value is safer if you don't want the lambda function to modify the original variables, while capturing by reference allows you to modify the original variables from within the lambda function.

Example 3: C++ Lambda Capture by Value:

Let's look at an example that demonstrates capturing variables by value in a lambda function:

  • C++

C++

#include <iostream>

int main() {

int x = 10;

int y = 20;

auto lambda = [x, y]() {

   std::cout << "Inside lambda: x = " << x << ", y = " << y << std::endl;

};

x = 15;

y = 25;

lambda();

std::cout << "Outside lambda: x = " << x << ", y = " << y << std::endl;

return 0;

}


In this example:

  1. We declare two integer variables x and y and initialize them with values 10 and 20, respectively.
     
  2. We define a lambda function lambda that captures x and y by value using the capture clause [x, y].
     
  3. Inside the lambda function, we print the values of x and y using std::cout.
     
  4. After defining the lambda function, we modify the values of x and y to 15 and 25, respectively.
     
  5. We invoke the lambda function lambda(), which executes the code inside the lambda function.
     
  6. Finally, we print the values of x and y outside the lambda function.
     

Output:

Inside lambda: x = 10, y = 20
Outside lambda: x = 15, y = 25


In this example, the lambda function captures x and y by value. When the lambda function is defined, it creates its own copies of x and y based on their values at the point of capture.

Even though we modify the values of x and y after defining the lambda function, the values inside the lambda function remain unchanged. The lambda function uses the captured values of x and y, which are 10 and 20, respectively.

When we print the values of x and y outside the lambda function, we see the modified values 15 and 25, respectively. This demonstrates that capturing by value creates independent copies of the variables within the lambda function, and any modifications to the original variables do not affect the captured values.

Note : Capturing by value is useful when you want to preserve the values of variables at the point of capture and ensure that the lambda function operates on its own copies of the variables.

Example 4: C++ Lambda Capture by Reference:

#include <iostream>

  • C++

C++

int main() {

int x = 10;

int y = 20;

auto lambda = [&x, &y]() {

   x = 15;

   y = 25;

   std::cout << "Inside lambda: x = " << x << ", y = " << y << std::endl;

};

lambda();

std::cout << "Outside lambda: x = " << x << ", y = " << y << std::endl;

return 0;

}


In this example:

  1. We declare two integer variables x and y and initialize them with values 10 and 20, respectively.
     
  2. We define a lambda function lambda that captures x and y by reference using the capture clause [&x, &y].
     
  3. Inside the lambda function, we modify the values of x and y to 15 and 25, respectively.
     
  4. We also print the values of x and y inside the lambda function using std::cout.
     
  5. We invoke the lambda function lambda(), which executes the code inside the lambda function.
     
  6. Finally, we print the values of x and y outside the lambda function.
     

Output:

Inside lambda: x = 15, y = 25
Outside lambda: x = 15, y = 25


In this example, the lambda function captures x and y by reference. When the lambda function is defined, it captures references to the original variables x and y.

Inside the lambda function, we modify the values of x and y to 15 and 25, respectively. Since x and y are captured by reference, any modifications made to them inside the lambda function affect the original variables.

When we print the values of x and y inside the lambda function, we see the modified values 15 and 25, respectively. Similarly, when we print the values of x and y outside the lambda function, we also see the modified values 15 and 25.

This demonstrates that capturing by reference allows the lambda function to directly access and modify the original variables. Changes made to the captured variables inside the lambda function are reflected in the original variables.

Capturing by reference is useful when you want the lambda function to be able to modify the original variables and have those changes persist outside the lambda function.

Note : However, it's important to be cautious when capturing by reference, as it can lead to unexpected behavior if the original variables go out of scope or are modified unexpectedly.

Example: C++ Lambda Function as Argument in STL Algorithm:

Lambda functions are often used as arguments to STL algorithms, providing a concise and expressive way to define custom behavior. Let's look at an example that demonstrates using a lambda function as an argument in an STL algorithm:

  • C++

C++

#include <iostream>

#include <vector>

#include <algorithm>

int main() {

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int threshold = 5;

auto lessThanThreshold = [threshold](int num) {

   return num < threshold;

};

auto it = std::find_if(numbers.begin(), numbers.end(), lessThanThreshold);

if (it != numbers.end()) {

   std::cout << "First number less than " << threshold << " is: " << *it << std::endl;

} else {

   std::cout << "No number less than " << threshold << " found." << std::endl;

}

return 0;

}


In this example:

  1. We have a vector of integers called numbers containing values from 1 to 10.
     
  2. We define an integer variable threshold with a value of 5.
     
  3. We define a lambda function lessThanThreshold that captures threshold by value and takes an integer parameter num. The lambda function returns true if num is less than threshold, and false otherwise.
     
  4. We use the std::find_if function from the <algorithm> header to find the first element in the numbers vector that satisfies the condition specified by the lessThanThreshold lambda function.
     
  5. The find_if function returns an iterator to the first element that satisfies the condition. If no such element is found, it returns the end iterator of the vector.
     
  6. We check if the returned iterator it is not equal to numbers.end(), indicating that an element less than threshold was found.
     
  7. If an element is found, we print the value of the element using *it.
     
  8. If no element is found, we print a message indicating that no number less than threshold was found.
     

Output

First number less than 5 is: 1


In this example, the lambda function lessThanThreshold is used as an argument to the find_if algorithm. The find_if function calls the lambda function for each element in the numbers vector until the lambda function returns true or until the end of the vector is reached.

The lambda function captures threshold by value and compares each number num with threshold. If num is less than threshold, the lambda function returns true, indicating that the condition is satisfied.

Note : With the help of lambda functions as arguments to STL algorithms, you can easily customize the behavior of the algorithms based on your specific requirements without the need to write separate named functions.

Frequently Asked Questions

What are lambda expressions used for in C++?

Lambda expressions are used to create anonymous functions that can be defined inline, often passed as arguments to functions or used for defining quick operations without the need for separate function declarations.

Can a lambda expression capture any kind of variable?

A lambda can capture variables by value or by reference. It can capture any variable that's visible in the scope where the lambda is defined, but the method of capture will affect whether the lambda can modify the variable.

How does capturing by reference differ from capturing by value in lambda expressions?

Capturing by reference allows the lambda to modify the original variable and reflect changes outside the lambda’s scope. Capturing by value creates a copy of the variable, and changes inside the lambda do not affect the original variable.

Conclusion 

In this article, we have learned about lambda expressions in C++, which are a powerful feature introduced in C++11. Lambda expressions allow us to define anonymous functions inline, which provides an easy but expressive way to write small, one-time-use functions. We also discussed the syntax and usage of lambda expressions, which has how to capture variables, pass parameters, specify return types, and use them with STL algorithms. Lambda expressions offer flexibility, readability, and the ability to encapsulate functionality, making them a valuable tool in modern C++ programming.

You can refer to our guided paths on the Coding Ninjas. You can check our course to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. Also, check out some of the Guided Paths on topics such as Data Structure andAlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry.

Live masterclass