Lambda Expressions (C++11 and Later) in C++
Lambda expressions, introduced in C++11, are a powerful feature that allow developers to define anonymous functions inline. They are particularly useful in situations where a function is needed temporarily or as a callback. Lambda expressions provide a concise way to express function objects, making code easier to read and write.
What is a Lambda Expression?
A lambda expression in C++ is an anonymous function that can be defined inline, without the need for a separate function declaration. It allows the programmer to create function objects directly in the code. Lambda expressions are particularly useful for short-lived functions or where the function is used only once.
The basic syntax of a lambda expression is:
[ captures ] ( parameters ) -> return_type { function_body }
Let's break it down:
- Captures: This part allows the lambda to capture variables from the surrounding scope. It can capture by value or by reference.
- Parameters: This is the list of parameters for the lambda, similar to a function's argument list.
- Return Type: This is optional and specifies the return type of the lambda. If omitted, the return type is inferred.
- Function Body: The body contains the actual code to be executed when the lambda is called.
Simple Lambda Expression Example
Here is a simple example that demonstrates how to define and use a lambda expression:
#include <iostream> using namespace std; int main() { // Define a lambda that adds two integers auto add = [](int a, int b) -> int { return a + b; }; // Call the lambda int result = add(3, 4); cout << "Result: " << result << endl; // Output: Result: 7 return 0; }
In this example, the lambda expression [](int a, int b) -> int { return a + b; }
defines a function that takes two integers as parameters and returns their sum. The auto
keyword is used to deduce the type of the lambda, making it easier to work with.
Capturing Variables in Lambdas
Lambdas can capture variables from the surrounding scope. This is useful when you want to use variables that are not passed as parameters to the lambda. There are different ways to capture variables, including by value, by reference, and a combination of both.
Capture by Value
In this case, the lambda captures a copy of the variables from the surrounding scope:
#include <iostream> using namespace std; int main() { int x = 10; // Lambda that captures x by value auto lambda = [x]() { return x + 5; }; // Calling the lambda cout << "Result: " << lambda() << endl; // Output: Result: 15 return 0; }
The lambda captures x
by value, so it uses the value of x
at the time the lambda is created. Changes to x
after the lambda is created do not affect the result of the lambda.
Capture by Reference
When capturing by reference, the lambda will use the actual variables from the surrounding scope:
#include <iostream> using namespace std; int main() { int x = 10; // Lambda that captures x by reference auto lambda = [&x]() { x += 5; }; // Calling the lambda lambda(); cout << "New value of x: " << x << endl; // Output: New value of x: 15 return 0; }
In this example, the lambda captures x
by reference, so any changes made to x
inside the lambda will affect the original variable.
Capture by Value and Reference
It is also possible to capture variables by both value and reference:
#include <iostream> using namespace std; int main() { int x = 10; int y = 20; // Lambda that captures x by value and y by reference auto lambda = [x, &y]() { return x + y; }; // Calling the lambda cout << "Result: " << lambda() << endl; // Output: Result: 30 y = 30; // Change y cout << "Result after changing y: " << lambda() << endl; // Output: Result after changing y: 30 return 0; }
In this case, the lambda captures x
by value and y
by reference. After the lambda is called, modifying y
affects the result when the lambda is invoked again, since y
was captured by reference.
Lambda Expressions with No Parameters
Lambdas can also be defined with no parameters. This is useful when you want to create a simple function without any input data:
#include <iostream> using namespace std; int main() { // Lambda with no parameters auto greet = []() { cout << "Hello, World!" << endl; }; // Call the lambda greet(); // Output: Hello, World! return 0; }
This lambda does not take any parameters and simply prints a greeting when called.
Lambda Expressions with Return Types
If a lambda has a return value, you can explicitly specify the return type. If the return type is omitted, the compiler will attempt to deduce it:
#include <iostream> using namespace std; int main() { // Lambda with explicit return type auto multiply = [](int a, int b) -> int { return a * b; }; // Calling the lambda cout << "Multiplication result: " << multiply(4, 5) << endl; // Output: Multiplication result: 20 return 0; }
Here, the lambda explicitly specifies a return type of int
, indicating that it will return an integer. The lambda multiplies two numbers and returns the result.
Using Lambdas with STL Algorithms
Lambdas are frequently used in conjunction with Standard Template Library (STL) algorithms such as std::sort
, std::for_each
, and std::find_if
to provide concise, inline custom functionality.
Example: Using Lambda with std::sort
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> vec = {5, 2, 8, 1, 9}; // Sorting the vector in descending order using a lambda expression sort(vec.begin(), vec.end(), [](int a, int b) -> bool { return a > b; }); for (int n : vec) { cout << n << " "; } cout << endl; // Output: 9 8 5 2 1 return 0; }
In this example, a lambda is passed to std::sort
to define a custom comparison for sorting the vector in descending order.
Conclusion
Lambda expressions in C++ provide a powerful and flexible way to define anonymous functions that can be used inline, often in combination with STL algorithms. Their ability to capture variables, provide concise code, and simplify the use of function objects makes them a valuable feature in modern C++ programming.