Lambda Expression in C++
C++ 11 introduced lambda expressions to allow inline functions which can be used for short snippets of code that are not going to be reused. Therefore, they do not require a name. They are mostly used in STL algorithms as callback functions.
Example:
// Defining a lambda
auto res = [](int x) {
return x + x;
};
cout << res(5);
Output
10
The lambda expression in the above program takes an integer x as input and returns the sum of x with itself. The result of res(5) prints 10, as the lambda doubles the value of 5.
Syntax
[capture-clause] (parameters) -> return-type {
// definition
}
To learn how to use lambda expressions effectively in your C++ programs, the C++ Course provides detailed explanations and examples.
Return Type
Generally, the return-type in lambda expressions is evaluated by the compiler itself and we don’t need to specify it explicitly. However, in some complex cases e.g. conditional statements, the compiler can’t determine the return type and explicit specification is required.
Parameters
These parameters are similar to the function parameters in every way.
Capture Clause
A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope. We can capture external variables from the enclosing scope in three ways using capture clause:
- [&]: capture all external variables by reference.
- [=]: capture all external variables by value.
- [a, &b]: capture ‘a’ by value and ‘b’ by reference.
A lambda with an empty capture clause [] can only access variables which are local to it.
vector<int> v1, v2;
// Capture v1 and v2 by reference
auto byRef = [&] (int m) {
v1.push_back(m);
v2.push_back(m);
};
// Capture v1 and v2 by value
auto byVal = [=] (int m) mutable {
v1.push_back(m);
v2.push_back(m);
};
// Capture v1 by reference and v2 by value
auto mixed = [=, &v1] (int m) mutable {
v1.push_back(m);
v2.push_back(m);
};
// Push 20 in both v1 and v2
byRef(20);
// Push 234 in both v1 and v2
byVal(234);
// Push 10 in both v1 and v2
mixed(10);
Output
20 10 20
Let’s understand what happened in this program:
- byRef captures all by reference. So pushing 20 will push it into original v1 and v2.
- byVal captures all by value. So pushing 234 will not do anything to original vectors.
- mixed captures v1 by reference and v2 by value. So pushing 10 will only push it into v1.
The mutable keyword here is used in capture by value lambdas only because, by default, value captured objects are const.
Examples
Lamda expressions are extensively used in STL in place of callback i.e. functions passed as arguments. The below examples demonstrate that:
Sort Vector in Descending Order
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> v = {5, 1, 8, 3, 9, 2};
// Sort in descending order
sort(v.begin(), v.end(), [] (const int& a, const int&b) {
return a > b;
});
for (int x : v)
cout << x << " ";
return 0;
}
Output
9 8 5 3 2 1
Find First Number Divisible by 3
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> v = {5, 1, 8, 3, 9, 2};
// Sort in descending order
auto it = find_if(v.begin(), v.end(), [] (const int& a) {
return a % 3 == 0;
});
if (it != v.end()) cout << *it;
else cout << "No such element";
return 0;
}
Output
3
Applications
Lambda expressions’ main purpose was to replace the functions in callbacks by providing inline definitions. Following are the common applications of lambda expressions in C++
- Inline, Anonymous Functions: Write small functions directly where needed without naming them.
- STL Algorithms: Pass custom comparison or transformation logic to algorithms like sort, for_each, etc.
- Callbacks and Event Handling: Use lambdas as callbacks for asynchronous operations or event handlers.
- Threading and Concurrency: Pass lambdas to threads for quick, inline tasks without defining separate functions.
- Custom Comparators in Containers: Use lambdas as comparators for containers like priority_queue, set, etc.
C++ Lambda Expression – FAQs
How do you capture variables in a lambda?
Use [=] for by-value, [&] for by-reference, or [x]/[&x] for specific variables.
Can a lambda modify value captured variables?
By default, no, but you can use mutable to modify by-value captures. But these changes will not be done on original variable.
When should I avoid using lambdas?
Avoid lambdas for complex logic; prefer regular functions or named function objects for readability.
Can lambdas have default arguments?
Yes, just like regular functions, lambdas can have default value arugments.