Tutorials Logic, IN info@tutorialslogic.com

C++ Lambda Expressions and Capture Lifetime

Closure State

A lambda creates a callable object with an optional capture list. Capturing by value copies selected state into the closure; capturing by reference keeps access to the original object. The central safety question is whether every referenced object will still exist when the lambda runs.

Capture Only What the Predicate Needs

An explicit threshold capture communicates the dependency and avoids accidentally retaining unrelated local state.

Filter values with a captured threshold

Filter values with a captured threshold
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    const std::vector<int> values{3, 12, 8, 20};
    const int threshold = 10;
    std::for_each(values.begin(), values.end(), [threshold](int value) {
        if (value >= threshold) std::cout << value << '\n';
    });
}
Output
12
20

Mutable and Generic Lambdas

mutable permits modification of a value-captured copy, not the original variable. An auto parameter creates a generic lambda whose call operator is a template. Use both only when they clarify the local operation.

  • Prefer explicit captures for stored callbacks.
  • Never return a lambda that references dead locals.
  • Use init-capture to move ownership into a callback.

Capture Lifetime

Use explicit captures for callbacks that outlive the current statement. A blanket [&] can leave dangling references when stored or executed asynchronously. Capturing this stores a pointer, not a copy of the object; use an ownership-aware strategy such as a weak_ptr when callback lifetime may exceed the owner.

mutable allows a value-captured copy to change inside the closure without changing the original. Generic lambdas use auto parameters and can express small local algorithms. Keep a lambda short enough that its captures and result remain obvious; promote substantial reusable behavior to a named function object or function.

Choose Captures from Lifetime Requirements

A reference capture is safe only while the referenced object remains alive. When a callback escapes the creating scope, capture the required value, move an owned resource into the closure, or use a lifetime-aware smart pointer. Avoid broad default captures in long-lived callbacks because they hide what the closure owns.

Use mutable only when a value-captured closure intentionally maintains private state. That state belongs to the closure object and is not a write back to the original variable.

Return a Closure with Owned State

Return a Closure with Owned State
#include <iostream>

auto make_counter(int start) {
    return [value = start]() mutable { return ++value; };
}

int main() {
    auto counter = make_counter(4);
    std::cout << counter() << ' ' << counter() << '\n';
}
Output
5 6

The closure owns its captured value, so it remains valid after make_counter returns.

Before you move on

Capture Review

5 checks
  • Every capture has a purpose.
  • Referenced objects outlive the lambda call.
  • Stored callbacks own required state.
  • mutable changes only a deliberate copy.
  • A named function is used when reuse or testing warrants it.

Lambda Capture Boundary

  • Dangling reference capture

    Do not return or store a lambda that captures a short-lived local by reference. Capture an owned value or ensure the referenced object outlives every invocation.

Try this next

Lambda Lifetime Practice

0 of 2 completed

  1. Write a function that returns a lambda, first capture a local by reference, then replace it with a value capture and explain the lifetime difference. Invoke the returned lambda only after the factory function has returned.
  2. Sort records by score and then by name using a lambda comparator that satisfies strict weak ordering. Equal records must never compare less in either direction.

Lambda Questions

It becomes unsafe when the lambda outlives the captured local variable, such as after being stored for later execution.

It allows the lambda to modify its own copies of value-captured variables. The original variables remain unchanged.

They keep a small predicate or transformation next to the algorithm call without requiring a separate named function.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.