C++ Cheatsheet
Lambdas
Use this C++ reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Syntax
[ capture-list ] ( parameters ) specifiers -> return-type { body }All parts except the capture list and body are optional.
auto greet = []() { std::cout << "hello\n"; }; greet(); auto add = [](int a, int b) { return a + b; }; // return type deduced add(2, 3); // 5 auto square = [](double x) -> double { return x * x; }; // explicit return type
Capture List
Captures give the lambda access to variables from the enclosing scope.
int x = 10, y = 20; // Capture by value (copy made at lambda creation time) auto f1 = [x]() { return x; }; // x captured by value auto f2 = [=]() { return x + y; }; // all local variables captured by value // Capture by reference auto f3 = [&x]() { x = 99; }; // x captured by reference auto f4 = [&]() { x = 1; y = 2; }; // all locals by reference (dangerous if lambda outlives scope) // Mixed auto f5 = [=, &x]() { return x + y; }; // everything by value, except x by ref auto f6 = [&, x]() { return x + y; }; // everything by reference, except x by value // Capture this (member variables) struct Foo { int val = 42; auto getLambda() { return [this]() { return val; }; // capture this by pointer // return [*this]() { return val; }; // capture this by value (C++17) // return [=, this]() { return val; }; // C++20 (= no longer implies this) } }; // Init capture / generalized capture (C++14) — create new variable in capture int z = 5; auto f7 = [w = z * 2]() { return w; }; // w = 10, stored in closure auto f8 = [v = std::move(vec)]() { return v.size(); }; // move into lambda auto f9 = [ptr = std::make_unique<int>(7)]() { return *ptr; }; // unique_ptr in lambda
mutable Lambdas
By default, value captures are const inside the body. Use mutable to allow modification (modifies the copy inside the closure, not the original).
int n = 0; auto counter = [n]() mutable { return ++n; }; // n in closure incremented counter(); // 1 counter(); // 2 // n in outer scope is still 0 // Without mutable: auto bad = [n]() { return ++n; }; // compile error: n is const
Lambda as std::function and Template Parameter
#include <functional> std::function<int(int, int)> f = [](int a, int b){ return a + b; }; f(2, 3); // 5 // Prefer auto over std::function (no type erasure overhead) auto g = [](int a, int b){ return a + b; }; // Pass to algorithm std::vector<int> v = {3, 1, 4, 1, 5}; std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; }); // descending // Higher-order functions auto applyTwice = [](auto f, auto x) { return f(f(x)); }; applyTwice([](int n){ return n * 2; }, 3); // 12
Generic Lambdas (C++14)
auto parameters make a lambda a template:
auto add = [](auto a, auto b) { return a + b; }; add(1, 2); // int add(1.0, 2.0); // double add(std::string("a"), "b"); // string // Explicit template parameter (C++20) auto typed = []<typename T>(T a, T b) { return a + b; }; typed(1, 2); typed(1.0, 2.0); // Constrain with concepts (C++20) auto intOnly = []<std::integral T>(T a, T b) { return a + b; };
Immediately Invoked Lambda Expression (IILE)
// Complex initialization in one expression const int result = [&]() { if (x > 0) return x * 2; if (y > 0) return y * 3; return 0; }(); // Init a const with a switch-like expression const std::string name = [](int code) -> std::string { switch (code) { case 1: return "alpha"; case 2: return "beta"; default: return "unknown"; } }(statusCode);
Recursive Lambdas
Lambdas cannot refer to themselves by name directly. Use one of:
// Method 1: std::function (slower — type erasure) std::function<int(int)> fib = [&fib](int n) -> int { return n <= 1 ? n : fib(n-1) + fib(n-2); }; fib(10); // Method 2: Pass self as parameter (C++14+) auto fib2 = [](auto self, int n) -> int { return n <= 1 ? n : self(self, n-1) + self(self, n-2); }; fib2(fib2, 10); // Method 3: Explicit this parameter (C++23 deducing-this) auto fib3 = [](this auto self, int n) -> int { return n <= 1 ? n : self(n-1) + self(n-2); }; fib3(10);
noexcept and constexpr Lambdas
auto safe = []() noexcept { return 42; }; auto ce = []() constexpr { return 42; }; // implicitly constexpr if possible (C++17) constexpr auto val = ce(); // evaluated at compile time // Explicit constexpr (C++17) auto f = [](int x) constexpr { return x * 2; }; static_assert(f(5) == 10);
Storing Lambdas and Type Deduction
// Each lambda has a unique, unnamed type — use auto auto f = [](int x){ return x; }; // Store heterogeneous lambdas in std::function std::function<void()> handlers[] = { []{ std::cout << "A\n"; }, []{ std::cout << "B\n"; } }; // std::function has overhead; for homogeneous sets, use function pointers if stateless int (*fp)(int) = [](int x){ return x * 2; }; // stateless lambda → function pointer // Type of lambda (for template, decltype) auto myLambda = [](int x){ return x; }; using LT = decltype(myLambda);
Lambdas in STL Algorithms
#include <algorithm> #include <vector> std::vector<int> v = {1, -2, 3, -4, 5}; int threshold = 2; // Remove negatives std::erase_if(v, [](int x){ return x < 0; }); // C++20 // Sort by absolute value std::sort(v.begin(), v.end(), [](int a, int b){ return std::abs(a) < std::abs(b); }); // Count elements above threshold (capture by value) int cnt = std::count_if(v.begin(), v.end(), [threshold](int x){ return x > threshold; }); // Transform in-place std::transform(v.begin(), v.end(), v.begin(), [](int x){ return x * x; }); // Reduce with custom accumulator int product = std::accumulate(v.begin(), v.end(), 1, [](int acc, int x){ return acc * x; }); // Find first negative auto it = std::find_if(v.begin(), v.end(), [](int x){ return x < 0; });
Common Gotchas
// 1. Dangling reference capture auto bad_lambda() { int local = 5; return [&local]() { return local; }; // UB: local destroyed on return } // Fix: capture by value, or use init capture to move // 2. Capturing this when object may be destroyed struct Widget { std::function<void()> callback_; void setup() { callback_ = [this]{ use(val_); }; // dangerous if Widget is destroyed first // Fix: callback_ = [w = weak_from_this()]{ if (auto s = w.lock()) s->use(s->val_); }; } int val_ = 0; }; // 3. Default capture [=] in member functions captures 'this', not members directly struct S { int x = 5; auto f() { return [=]{ return x; }; // actually captures 'this'; x is this->x } }; // 4. Forgetting mutable for stateful value captures int count = 0; auto inc = [count]() { return ++count; }; // error: count is const auto inc2 = [count]() mutable { return ++count; }; // OK: modifies closure copy