Introduction
Lambda expressions are a powerful feature introduced in C++11, enabling developers to write inline, anonymous functions. They provide a concise way to define small pieces of code that can be passed as arguments or used wherever a function pointer is needed. This tutorial explores the concept of lambda expressions, their syntax, and practical use cases.
What Are Lambda Expressions?
Lambda expressions originate from functional programming paradigms and are designed for writing brief, unnamed functions. In C++, they are particularly useful for scenarios where defining a full functor (a class with an operator()
) would be excessive or cumbersome. Lambdas enhance code readability by keeping logic localized to the point of use.
Basic Syntax
A lambda expression in C++ consists of three main parts:
- Capture List:
[ ]
- Parameter List:
( )
- Function Body:
{ }
[]() { /* function body */ }
Example: A Simple Lambda
#include <iostream>
#include <vector>
#include <algorithm>
void processVector(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int n) {
std::cout << n << " ";
});
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
processVector(numbers);
return 0;
}
In this example, the lambda is used to print each element of a vector. The capture list []
is empty because no external variables are needed.
Capture List
The capture list specifies which variables from the surrounding scope should be available inside the lambda and how they should be captured (by value or by reference).
- By Value:
[x]
- By Reference:
[&x]
- Capture All by Reference:
[&]
- Capture All by Value:
[=]
void modifyVector(std::vector<int>& v, int addend) {
std::transform(v.begin(), v.end(), v.begin(),
[addend](int n) { return n + addend; });
}
Here, addend
is captured by value, allowing the lambda to use it without modifying the original variable.
Return Types
Lambda expressions can deduce return types based on their body. For simple cases:
auto square = [](int x) { return x * x; };
For more complex scenarios, you can specify the return type explicitly:
[](double d) -> double {
if (d < 0.0001) return 0;
else return d;
}
Advanced Features
Capturing Variables
Lambdas can capture variables from their enclosing scope, allowing them to use these variables within the lambda body.
void transformVector(std::vector<double>& v, double epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
return (d < epsilon) ? 0 : d;
});
}
Mutable Lambdas
If a lambda needs to modify captured variables by value, it can be marked as mutable
.
int main() {
int x = 10;
auto increment = [x]() mutable {
++x;
return x;
};
std::cout << increment() << " "; // Outputs: 11
std::cout << increment() << " "; // Outputs: 12
}
Use Cases
Lambda expressions are particularly useful in the Standard Template Library (STL) algorithms like std::for_each
, std::transform
, and others, where they replace verbose functor classes. They streamline code by keeping logic close to its usage point.
Conclusion
Lambda expressions significantly enhance C++’s expressiveness and conciseness. By understanding their syntax and capabilities, developers can write cleaner, more maintainable code, especially when working with STL algorithms or any situation requiring inline function definitions.