C++ Lambda Basics | Capture· mutable
Introduction: Sort without a functor class?
“One line of comparison—why a whole class?”
Before C++11, std::sort needed a functor with operator()—verbose for a one-liner.
Lambda: a closure you define at the call site: capture -> ret { body }.
Definition: A lambda is a nameless function object created where you need a callable—ideal for sort, find_if, thread entry points.
Replace functor:
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
Table of contents
- Lambda syntax
- Capture modes
- mutable and noexcept
- Generic lambdas (C++14)
- Recursive lambdas
- Examples
- Common errors
- Best practices
- Production patterns
- Checklist
1. Basic syntax
// g++ -std=c++17 -o lambda_basic lambda_basic.cpp && ./lambda_basic
#include <iostream>
int main() {
auto add = [](int a, int b) -> int {
return a + b;
};
int result = add(3, 5);
std::cout << result << "\n";
return 0;
}
Return type can be omitted if deducible. IIFE (immediately invoked):
int result = [](int x) { return x * x; }(5);
2. Capture modes
- []: nothing
- [=]: copy all used automatic variables by value (default capture by value)
- [&]: reference all used automatic variables
- [x, &y]: mixed
- [this]: capture
this(C++17: *[this] copies the object) Thread/async: prefer by-value capture of what the async work needs—never reference to stack unless it definitely outlives the call.
3. mutable
By-value captures are const in operator() unless the lambda is mutable—then you can mutate the copies inside the closure, not the originals.
4. Generic lambdas (C++14)
auto print = [](auto value) {
std::cout << value << "\n";
};
C++20: explicit template parameters on lambdas:
auto f = []<typename T>(T value) { /* ... */ };
5. Recursive lambdas
Use std::function and capture by reference, or Y-combinator style—beware value-capture of uninitialized std::function.
std::function<int(int)> factorial;
factorial = [&factorial](int n) -> int {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
6. Examples
Putting captures, mutable, and generic lambdas together in code you’d actually write:
#include <algorithm>
#include <vector>
#include <numeric>
std::vector<int> nums = {5, 3, 8, 1, 9, 2};
// Capture by reference to sort in place, generic (C++14) comparator
std::sort(nums.begin(), nums.end(), [](const auto& a, const auto& b) {
return a < b;
});
// Capture by value + mutable: a stateful counter with its own copy of state
int threshold = 4;
auto count_above = [threshold, calls = 0](int x) mutable {
++calls;
return x > threshold;
};
int above = std::count_if(nums.begin(), nums.end(), count_above);
// Capture by reference to accumulate into an outer variable
long total = 0;
std::for_each(nums.begin(), nums.end(), [&total](int x) { total += x; });
Each of these mirrors a real pattern: value capture for a self-contained predicate you might reuse or pass around, mutable capture when the lambda needs private per-call state without touching the outer scope, and reference capture when the whole point is to affect a variable outside the lambda.
7. Common errors
- Dangling reference:
[&]to locals, lambda runs later → capture by value or extend lifetime - Loop variable
i: use [i] not [&i] in parallel work - mutable needed to increment by-value members
this/[*this]for async—object lifetimestd::function+ inline: type erasure can prevent inlining and allocate
8. Best practices
- Prefer template parameters
template<typename F>overstd::functionwhen you don’t need type erasure - Minimal capture lists—avoid
[=]/[&]if you might add variables later unintentionally - noexcept when the lambda never throws (helps move/vector reallocation)
9. Production patterns
- Background work: std::thread(data = std::move(data){ … });
- ScopeGuard with lambdas for cleanup
- Callbacks: value capture or
shared_ptrfor shared ownership
10. Checklist
- Async/thread: value capture or proven lifetime
- Loop index: by-value in loop lambdas
-
find/sortpredicates:const¶meters for large objects
Related posts
Keywords
C++ lambda, lambda expression, capture, mutable, generic lambda, sort, find_if, dangling reference
Summary
| Item | Syntax |
|---|---|
| By-value default | [=] |
| By-reference default | [&] |
| Selective | [x, &y] |
this | [this] / [*this] (C++17) |
mutable | []() mutable { } |
Principles: short local logic; watch lifetimes; prefer templates over std::function when possible. | |
| Next: STL map and set #10-2 | |
| Previous: Variadic templates #9-3 |
FAQ
Lambda vs free function performance?
A. Lambdas passed as template arguments inline like free functions; std::function may heap-allocate and type-erase. One-line summary: Lambdas give local, optimizable callables for STL and concurrency—mind capture lifetimes.
References
Related posts
Related Articles (Internal Links)
Other articles related to this topic.
- C++ 람다 표현식 | [=]·[&] 캡처와 sort·find_if에서 람다 활용법
- C++ 람다 심화 | 초기화 캡처·완벽 전달·IIFE·재귀 람다와 실전 패턴
- C++ 함수 객체 | ‘Functor’ 완벽 가이드
Keywords Covered in This Article (Related Search Terms)
This article covers C++, Lambda, lambda expression, capture, mutable, Generic Lambda.