Lambda Expressions
Master lambda expressions for creating inline anonymous functions with captures for powerful, concise code
Learn how to create inline anonymous functions using lambda expressions - one of the most powerful features introduced in C++11.
A Simple Example
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> numbers{5, 2, 8, 1, 9, 3};
// Simple lambda - no captures, no parameters
auto printHello = []() {
std::cout << "Hello from lambda!" << "\n";
};
printHello();
// Lambda with parameters
auto square = [](int x) {
return x * x;
};
std::cout << "Square of 5: " << square(5) << "\n";
// Lambda with STL algorithm
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; }); // Descending order
// Lambda with auto return type
auto add = [](int a, int b) { return a + b; }; // return type deduced
// Explicit return type
auto divide = [](double a, double b) -> double {
return a / b;
};
// Using lambda inline
int count = std::count_if(numbers.begin(), numbers.end(),
[](int n) { return n > 5; });
std::cout << "Numbers > 5: " << count << "\n";
return 0;
}
Breaking It Down
Lambda Syntax: [](){} Structure
- What it does: Creates an anonymous function right where you need it
-
[]- Capture clause: Specifies which variables from surrounding scope to use -
()- Parameter list: Just like a regular function -
{}- Function body: The code to execute - Remember: Think of it as [capture](parameters){body}
Capture Clauses
-
[]- Captures nothing (most common for simple operations) -
[x]- Captures x by value (makes a copy) -
[&x]- Captures x by reference (uses the original) -
[=]- Captures all by value (convenient but dangerous) -
[&]- Captures all by reference (even more dangerous) -
[x, &y]- Mix and match: x by value, y by reference - Remember: Be explicit about what you capture to avoid bugs
Lambda with STL Algorithms
- What it does: Makes STL algorithms incredibly powerful and readable
- Before lambdas: Had to write separate named functions or function objects
- With lambdas: Write the logic inline where you use it
-
Example:
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); - Remember: This is where lambdas truly shine
Return Type Deduction
- What it does: Compiler automatically figures out the return type
-
Auto deduction:
[](int x) { return x * 2; }- returns int automatically -
Explicit type:
[](int x) -> double { return x * 2.0; }- specify when needed - Use explicit when: Multiple return paths with different types
- Remember: Most of the time auto deduction works perfectly
Why This Matters
- Lambdas changed C++ forever. Before C++11, using STL algorithms required writing separate functions or heavy function objects.
- Lambdas let you write the function right where you use it, capturing local variables naturally.
- They are essential for modern C++, async programming, and functional-style code.
Critical Insight
Lambda captures create a closure - a function that "remembers" the environment where it was created. [x] makes a copy that stays with the lambda forever. [&x] creates a reference that must stay valid. [=] and [&] are convenient but potentially dangerous - they capture everything in scope, which can lead to accidental captures. Be explicit when you can!
Also, lambdas are not function pointers - each lambda has a unique compiler-generated type. They can convert to function pointers only if they have no captures.
Best Practices
Be explicit with captures: Use [x, y] instead of [=] to clearly show which variables are captured and avoid accidental captures.
Use const references for large objects: [&vec] instead of [vec] avoids copying large containers when you only need to read them.
Prefer lambdas over function objects: For simple operations with STL algorithms, lambdas are more readable than separate function classes.
Use auto for lambda variables: Store lambdas in auto variables since each lambda has a unique compiler-generated type.
Common Mistakes
Dangling references: Capturing by reference [&x] when lambda outlives x leads to undefined behavior. The reference points to destroyed memory.
Accidental captures with [=] or [&]: Capturing everything can include unintended variables, leading to subtle bugs.
Modifying captured values: Values captured by [x] are const by default. Use [x]() mutable {} to modify copies.
Assuming lambdas are function pointers: Each lambda has a unique type. Only capture-less lambdas convert to function pointers.
Debug Challenge
This lambda tries to capture a local variable but the syntax is wrong. Click the highlighted line to fix it:
Quick Quiz
- What is the difference between [x] and [&x]?
- Can a lambda with captures be converted to a function pointer?
- What does [=] capture in a lambda?
Practice Playground
Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once