Lesson 49
Lambda Expressions (part 1)
Covers lambda syntax, capture lists, parameters, return types, mutable lambdas, and immediately invoked lambdas.
Lesson content
What is a Lambda Expression?
Lambda Expressions in C++ are one of the most powerful features introduced in C++11. They allow you to create anonymous functions, which are functions without a name. Instead of defining a separate function elsewhere in your program, you can write the function exactly where you need it.
Think of a lambda expression like writing a quick note on a sticky note instead of creating an entire notebook page. If the functionality is only needed once or in a small section of your code, a lambda expression keeps everything together and makes the code easier to read.
Before C++11, programmers often had to write small helper functions or create function objects (functors) just to perform simple operations like sorting or filtering data. This added unnecessary code and reduced readability. Lambda expressions solve this problem by allowing developers to define short functions inline.
Today, lambda expressions are used extensively throughout modern C++ programming. They are especially useful when working with the Standard Template Library (STL) algorithms such as std::sort, std::find_if, std::for_each, and many others.
Why do lambda expressions exist?
- Reduce unnecessary helper functions
- Keep related logic together
- Improve code readability
- Simplify callback functions
- Make STL algorithms easier to use
- Support functional programming techniques
Instead of creating a function, calling it, and never using it again elsewhere in the program, you simply write the function where it is needed.
Real-world use cases
- Sorting collections
- Searching data
- Event callbacks
- GUI programming
- Game development
- Multithreading
- Custom comparison functions
- Data processing pipelines
- Asynchronous programming
Prerequisites
- Variables
- Data types
- Functions
- Function parameters
- Return values
- Loops
- Conditional statements
- Arrays
- Basic STL containers (vector)
- Basic understanding of references
If you're comfortable writing normal C++ functions, you're ready to learn lambda expressions.
General syntax
A lambda expression is simply an unnamed function that can be written directly inside your code.
[capture](parameters) -> return_type
{
// function body
};A lambda expression contains four major parts:
- Capture list – specifies which outside variables the lambda can access
- Parameters – input values passed to the lambda
- Return type – specifies what the lambda returns (optional in many cases)
- Body – the code executed by the lambda
Basic syntax
[]()
{
// code
};
// Example:
[]()
{
std::cout << "Hello!";
};This creates a lambda function. Notice that it has no name, it can be stored in a variable, and it can be called immediately.
Storing a lambda
Most lambda expressions are stored using the auto keyword.
auto greet = []()
{
std::cout << "Hello";
};
// Now greet behaves just like a function. Calling it:
greet();
// Output: HelloLambda with parameters
Just like normal functions, lambdas can accept parameters.
auto add = [](int a, int b)
{
return a + b;
};
// Calling it:
std::cout << add(4, 7);
// Output: 11Return type
Usually, C++ automatically determines the return type.
auto square = [](int x)
{
return x * x;
};Sometimes you explicitly specify it. The arrow (->) specifies the return type.
auto divide = [](int a, int b) -> double
{
return static_cast<double>(a) / b;
};Capture list
The capture list is one of the most important parts of lambda expressions. It tells the lambda which outside variables it is allowed to use.
[capture]- [] – capture nothing
- [x] – capture x by value
- [&x] – capture x by reference
- [=] – capture everything by value
- [&] – capture everything by reference
- [=, &x] – capture all by value except x
Capture by value
The lambda receives its own copy.
int number = 10;
auto print = [number]()
{
std::cout << number;
};
// Even if number changes later, the lambda still uses the copied value.Capture by reference
The lambda works with the original variable.
int number = 10;
auto increase = [&number]()
{
number++;
};
// Now the original variable changes.Mutable lambdas
Normally, captured-by-value variables cannot be modified.
int x = 5;
auto func = [x]()
{
x++; // Error
};To modify the copied value, use mutable. The original variable remains unchanged.
auto func = [x]() mutable
{
x++;
};Immediately invoked lambda
A lambda can execute immediately after being created.
[]()
{
std::cout << "Executed immediately!";
}();
// The first parentheses create the lambda.
// The second parentheses call it immediately.Example 1: Your first lambda
#include <iostream>
int main()
{
auto greet = []()
{
std::cout << "Hello, World!";
};
greet();
return 0;
}
// Output: Hello, World!Here, [] means no variables are captured, () means no parameters, the lambda is stored in greet, and calling greet() executes the lambda.
Example 2: Lambda with parameters
#include <iostream>
int main()
{
auto multiply = [](int a, int b)
{
return a * b;
};
int result = multiply(6, 7);
std::cout << result;
return 0;
}
// Output: 42The lambda behaves almost exactly like a normal function equivalent to int multiply(int a, int b) { return a * b; }. The difference is that no separate function name is needed outside the current scope.
Example 3: Capturing variables
#include <iostream>
int main()
{
int value = 10;
auto show = [value]()
{
std::cout << value << std::endl;
};
value = 50;
show();
std::cout << value;
return 0;
}
// Output:
// 10
// 50The lambda captured the value before it changed. It stored its own copy, so later modifications to the original variable do not affect the captured value.