Lesson 51
Lambda Expressions (part 3)
Covers comparing lambdas to regular functions and functors, choosing the right callable type, a full recap of lambda concepts, and frequently asked questions with examples.
Lesson content
Lambda expressions vs regular functions vs function objects
Choosing the right callable type depends on your use case.
- Has a name: lambda expression – no (anonymous); regular function – yes; function object (functor) – yes
- Can capture local variables: lambda – yes; regular function – no; functor – yes (through member variables)
- Defined inline: lambda – yes; regular function – no; functor – no
- Best for short tasks: lambda – excellent; regular function – possible; functor – possible
- Reusable: lambda – limited; regular function – yes; functor – yes
- Works with STL algorithms: lambda – excellent; regular function – yes; functor – excellent
- Easy to read: lambda – usually; regular function – yes; functor – can be verbose
- Requires extra class: lambda – no; regular function – no; functor – yes
Which one should you use?
- One-time operation → lambda
- Complex reusable logic → regular function
- Stateful reusable object → functor
- STL algorithms → lambda
- Callback functions → lambda
- Large business logic → regular function
Summary
- Lambda expressions are anonymous functions introduced in C++11
- They allow you to write function logic exactly where it is needed
- Lambdas reduce the need for small helper functions
- The general syntax is [capture](parameters) -> return_type { // function body };
- The capture list determines which local variables the lambda can access
- Variables can be captured by value ([x]), by reference ([&x]), all by value ([=]), or all by reference ([&])
- The mutable keyword allows modification of variables captured by value
- Generic lambdas (introduced in C++14) use auto parameters
- Lambdas integrate naturally with STL algorithms such as std::sort, std::for_each, std::find_if, std::count_if, and std::transform
- Lambdas improve code readability by keeping related logic close together
- Complex or frequently reused logic is usually better implemented as a regular function
Key takeaways
- Lambda expressions are anonymous functions
- They are ideal for short, one-time operations
- Capture lists control access to surrounding variables
- Value capture copies variables
- Reference capture modifies original variables
- mutable allows changes to copied variables
- Generic lambdas increase flexibility
- STL algorithms become much cleaner with lambdas
- Avoid overly long or complex lambdas
- Prefer explicit captures over capturing everything
Frequently asked questions
What is a lambda expression in C++?
A lambda expression is an anonymous function that can be written directly inside your code. It is commonly used for short operations, callbacks, and STL algorithms without creating a separate named function.
Why should I use lambda expressions instead of regular functions?
Lambda expressions are useful when a function is only needed in one place. They make code shorter, easier to read, and keep related logic together. If the same functionality is needed in multiple places, a regular function is usually a better choice.
What is the purpose of the capture list?
The capture list specifies which variables from the surrounding scope the lambda can use. Without capturing a variable, the lambda cannot access it.
int x = 10;
auto print = [x]()
{
std::cout << x;
};What is the difference between capturing by value and by reference?
Capture by value creates a copy of the variable, so the original variable is not modified, which is safer in many cases. Capture by reference uses the original variable, so it can be modified, which is useful when updates are required.
int value = 5;
// Capture by value
auto byValue = [value]()
{
std::cout << value;
};
// Capture by reference
auto byReference = [&value]()
{
value++;
};Can lambda expressions return values?
Yes. Like regular functions, lambdas can return values.
auto square = [](int x)
{
return x * x;
};
std::cout << square(6);
// Output: 36Can a lambda call itself recursively?
Yes, but not directly. Since a lambda has no name, recursive calls require techniques such as using std::function or passing the lambda to itself.
#include <functional>
#include <iostream>
int main()
{
std::function<int(int)> factorial;
factorial = [&](int n)
{
if (n <= 1)
return 1;
return n * factorial(n - 1);
};
std::cout << factorial(5);
return 0;
}
// Output: 120Are lambda expressions faster than regular functions?
In most cases, performance is comparable. Modern C++ compilers often inline lambdas, meaning they replace the function call with the function body during compilation, reducing call overhead. Performance differences are usually negligible, so choose based on readability and maintainability.
Conclusion
Lambda Expressions in C++ are one of the most valuable additions to modern C++. They let you write concise, readable, and expressive code by defining small functions exactly where they are needed.
Whether you're sorting a vector, filtering data, handling callbacks, or working with STL algorithms, lambdas help reduce boilerplate and improve clarity. As you become more comfortable with modern C++, you'll find yourself using lambda expressions frequently in everyday programming.
The key is to use them thoughtfully: keep them short, capture only what you need, and switch to named functions when the logic becomes too large or is reused across your project.
Final cheat sheet
- [](){} – no captures, no parameters
- [](int x){} – parameterized lambda
- [x] – capture x by value
- [&x] – capture x by reference
- [=] – capture all variables by value
- [&] – capture all variables by reference
- [=, &x] – capture all by value except x by reference
- mutable – allow modification of value-captured variables
- -> int – explicit return type
- [](auto x) – generic lambda (C++14)
- [](){ }(); – immediately invoked lambda