Lambda Expressions
cpp2-3's std::sort works with any callable — this chapter introduces the concise way real C++ code writes one inline, and reveals it's built from a mechanism already covered.
Lambda Syntax
[capture](params) { body } — a lambda passed directly as std::sort's custom comparator, exactly cpp2-3's own algorithm-plus-callable pattern.
Capture Lists
The real new concept: how a lambda accesses variables from its surrounding scope.
[]— captures nothing[=]— captures everything used, by value (a copy)[&]— captures everything used, by reference[x]— captures justx, by value[&x]— captures justx, by reference
What a Lambda Actually Is — A Class With operator()
The real mechanism, connecting directly back to cpp1-6: a lambda is compiler-generated syntactic sugar for an anonymous class, with captured variables as member fields and an overloaded operator() containing the lambda's body. Not a new language feature at its core — cpp1-6's own mechanism, applied automatically. Capturing by value copies into those member fields at the point the lambda is created, not when it's called — a real, sometimes-surprising timing detail.
std::function
A type-erased wrapper capable of holding any callable matching a given signature — a lambda, a function pointer (c2-7's own material), or a class with operator(). Useful because every lambda genuinely has its own unique, compiler-generated type, even when two lambdas look identical in source — std::function gives them a common type to be stored or passed around as.
Contrasted With Rust's Own Closures
Rust closures work via the same underlying idea — the compiler generates a hidden struct capturing referenced variables, implementing one of the Fn/FnMut/FnOnce traits, genuinely comparable to operator(). The real difference: Rust's own capture rules are inferred automatically from how the closure body actually uses each variable (by reference, mutable reference, or move), rather than requiring an explicit capture-list syntax the way C++ does. A genuine ergonomic difference — but the underlying "anonymous callable object" mechanism is, once again, a real point of convergence, alongside RAII/Drop and monomorphization.
| Concept | C++ Lambda | Rust Closure |
|---|---|---|
| Underlying mechanism | an anonymous class with operator() | a hidden struct implementing Fn/FnMut/FnOnce |
| Capture specification | explicit — [x], [&x], [=], [&] | inferred automatically from usage |
| Each closure's type | unique, unnameable — needs std::function or auto | unique, unnameable — needs a generic bound or Box<dyn Fn> |
[x] over [=], and [&x] over [&] — both a performance consideration (avoiding unnecessary copies) and a safety consideration (fewer captured references means fewer chances of a dangling reference).
[&] or [&x]) that's stored and called after the enclosing function has returned holds a dangling reference — exactly cpp1-8's own dangling-reference warning, reached through a lambda's capture instead of an explicit reference variable.
Coding Challenges
Use std::sort with a lambda comparator to sort a std::vector
Write a lambda that captures a local int by value and one that captures the same variable by reference. After creating both lambdas, change the original variable's value, then call both lambdas and explain why they produce different results.
📄 View solutionExplain precisely why a lambda is described as "not a new language feature at its core," tying your answer directly back to cpp1-6's own operator overloading material and what a lambda actually compiles down to.
📄 View solutionChapter 7 Quick Reference
[capture](params) { body }— lambda syntax; used directly withcpp2-3's own algorithms[]/[=]/[&]/[x]/[&x]— capture nothing, everything by value, everything by reference, or specific variables- A lambda is compiler-generated sugar for a class with
operator()—cpp1-6's own mechanism, automated - By-value captures copy at lambda creation time, not call time
std::function— a type-erased wrapper for any callable, since every lambda's real type is unique and unnameable- Reference-capturing lambdas that outlive their captured variables dangle — the same bug class as
cpp1-8 - Next chapter: const-correctness and modern C++ style — closing Course 2