Advanced⏱️ 8 min📘 Topic 25 of 32

λ Lambda Expressions in C++ — Inline Functions with Captures

Master C++ lambda expressions — syntax, captures (by value vs reference), return types, generic lambdas and using lambdas with STL algorithms. With examples + interview Q&A.

A lambda is an inline, anonymous function — perfect for one-off callbacks passed to STL algorithms.

📜 Syntax

[capture](params) -> return_type { body }

The return type is usually inferred — just write [capture](params) { body }.

🎒 Captures — borrowing variables from the enclosing scope

  • [] — capture nothing
  • [x] — capture x by value (copy)
  • [&x] — capture x by reference
  • [=] — capture all by value
  • [&] — capture all by reference
  • [=, &x] — all by value except x by reference

📋 Real-world: STL algorithms

std::vector<int> v = {3, 1, 4, 1, 5, 9};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// v is now {9, 5, 4, 3, 1, 1}

🌍 Generic lambdas (C++14+)

auto add = [](auto a, auto b) { return a + b; };
add(1, 2);       // int
add(1.5, 2.5);   // double
add("hi", ""s);  // works for anything with operator+

⚠️ Capture-by-reference lifetime trap

If a lambda captures by reference and outlives the captured variables, it has dangling references.

💻 Code Examples

Sort descending

std::vector<int> v = {3, 1, 4, 1, 5};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
for (int n : v) std::cout << n << ' ';
Output:
5 4 3 1 1

Capture by value vs reference

int x = 10;
auto byVal = [x]() { return x + 1; };
auto byRef = [&x]() { return x + 1; };
x = 100;
std::cout << byVal() << ' ' << byRef();
Output:
11 101

Count items matching a predicate

std::vector<int> nums = {1, 2, 3, 4, 5, 6};
int threshold = 3;
int count = std::count_if(nums.begin(), nums.end(),
              [threshold](int n) { return n > threshold; });
std::cout << count;
Output:
3

⚠️ Common Mistakes

  • Capturing locals by reference and returning the lambda — dangling references when the locals die.
  • Using `[=]` blindly and copying expensive objects — capture only what you need.
  • Trying to capture `this` and modifying it without `mutable` — by-value captures are const by default.
  • Confusing lambda type — each lambda has a unique anonymous type. Use `auto` or `std::function` to store them.

🎯 Interview Questions

Real questions asked at top product and service-based companies.

Q1.What is a lambda expression?Intermediate
An anonymous function defined inline. Syntax: `[capture](params) -> ret { body }`. Used as throwaway callbacks for STL algorithms, event handlers, and simple closures.
Q2.What's the difference between [=] and [&]?Intermediate
[=] captures all referenced variables BY VALUE (copies). [&] captures all BY REFERENCE. Value is safer (no dangling) but copies; reference is cheap but lifetime-fragile.
Q3.Can a lambda modify captured variables?Intermediate
By-reference captures: yes, naturally. By-value captures: no by default — the lambda's operator() is const. Add `mutable` to allow modifying the captured copies (without affecting the originals).
Q4.How is a lambda different from std::function?Advanced
Each lambda has a unique anonymous type — small, no allocation, inlined. std::function is a type-erasing wrapper that can hold any callable with a given signature — adds heap allocation and indirect call. Use `auto` for lambdas, `std::function` only when you need to STORE/PASS lambdas across boundaries.
Q5.What is a generic lambda?Advanced
C++14+ feature: parameters declared `auto` make the lambda's operator() a template. `auto add = [](auto a, auto b) { return a + b; };` — works for any addable types, like a one-shot function template.

🧠 Quick Summary

  • Lambda = inline anonymous function with captures.
  • Syntax: [capture](params) { body }.
  • Capture by value [x] or reference [&x].
  • [=] / [&] capture all referenced names.
  • Use `auto` to store; `std::function` to type-erase across boundaries.
  • Beware capture-by-reference lifetimes.