Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A lambda expression in C++ is a syntactic shortcut for defining an anonymous function object (a closure) inline. When the compiler encounters a lambda expression, it generates a unique, unnamed class type that overloads the function call operator (operator()). The resulting object instantiated from this class is the closure, and any captured variables with automatic storage duration are stored as member data within it.
[capture_list] <template_parameter_list> requires_clause (parameter_list) mutable constexpr exception_specification -> return_type {
    // function body
}

Anatomical Breakdown

1. Capture Clause ([]) Also known as the lambda introducer, this defines the lexical environment of the closure. It specifies which variables from the enclosing scope are accessible inside the lambda body and their capture mechanism (by value or by reference). Crucially, only variables with automatic storage duration (local variables) are captured. Variables with static, thread-local, or global storage duration are not captured by the closure; they are accessed directly.
  • []: Empty capture. The lambda has no access to local variables in the enclosing scope. It can be implicitly converted to a function pointer if the parameter list and return type match exactly with the target function pointer type. The only exception to strict type matching is that a noexcept lambda can implicitly convert to a non-noexcept function pointer.
  • [=]: Default capture by value. Any local variable referenced in the body is copied into the closure. Note: [=] implicitly captures the this pointer by value. Because the pointer itself is copied, the enclosing object’s members are accessed by reference. This behavior frequently leads to dangling pointers if the closure outlives the enclosing object. In C++20, this implicit capture of this via [=] was deprecated (meaning compilers will emit a warning), but the capture still occurs and the exact same dangling pointer risk remains.
  • [&]: Default capture by reference. Any local variable referenced in the body is bound by reference.
  • [this]: Captures the this pointer by value. This is required to access class member variables within a lambda defined inside a member function, as member variables cannot be captured directly by name.
  • [*this]: Captures the enclosing object by value (C++17). This creates a copy of the current object within the closure, preventing dangling references when the closure outlives the original object.
  • [x, &y]: Explicit capture. x is captured by value, y is captured by reference.
  • [x = std::move(y)]: Init-capture (C++14). Allows the declaration and initialization of new variables within the closure state, enabling the capture of move-only types like std::unique_ptr.
2. Template Parameter List (<>) Introduced in C++20, explicit template parameters can be declared directly after the capture clause: []<typename T>(T arg) {}. 3. Requires Clause (requires) Introduced in C++20, this constrains the template parameters of a templated lambda. It evaluates compile-time conditions to restrict the types that can be passed to the lambda. It can appear immediately after the template parameter list or after the trailing return type. 4. Parameter List (()) Functions identically to a standard function parameter list. It can be omitted entirely if the lambda takes no arguments and utilizes no specifiers (like mutable or noexcept).
  • Generic Lambdas (C++14): Using auto in the parameter list causes the compiler to generate a templated operator().
5. mutable Specifier By default, the operator() of the compiler-generated closure class is qualified as const. Therefore, variables captured by value cannot be modified within the lambda body. The mutable keyword removes this const qualification, allowing modification of the closure’s internal state. 6. constexpr / consteval Explicitly declares the operator() as a constexpr function or an immediate function (consteval). Since C++17, lambdas are implicitly constexpr if their body satisfies the requirements of a constexpr function. 7. Exception Specification Indicates the exception guarantee of the lambda. Specifying noexcept (equivalent to noexcept(true)) provides a non-throwing guarantee. In C++, this does not prevent the function from throwing exceptions internally; rather, it guarantees that if an exception is thrown and escapes the operator(), std::terminate is called immediately. Conversely, the specification can be explicitly set to noexcept(false) to indicate that escaping exceptions are allowed. 8. Trailing Return Type (-> type) Explicitly defines the return type of the lambda. If omitted, the compiler deduces the return type based on the return statements within the body using standard auto type deduction rules. 9. Lambda Body ({}) The execution logic of the function object.

Compiler Translation Mechanics

To understand the internal mechanics, consider how the compiler translates a lambda expression into a functor. Lambda Expression:
int multiplier = 5;
auto multiply_and_add = [multiplier](int a) mutable -> int {
    multiplier++;
    return a * multiplier;
};
Compiler-Generated Equivalent:
class __Lambda_Unnamed_1 {
    int multiplier; // Captured state becomes member data

public:
    // Constructor initializes captured variables
    inline __Lambda_Unnamed_1(int _multiplier) : multiplier(_multiplier) {}

    // operator() contains the lambda body
    // It is non-const because of the 'mutable' specifier
    inline int operator()(int a) {
        multiplier++;
        return a * multiplier;
    }
};

// Instantiation of the closure object
int multiplier = 5;
__Lambda_Unnamed_1 multiply_and_add(multiplier);

Generic and Templated Lambdas

C++14 introduced generic lambdas using auto, which translates to a member template operator(). C++20 expanded this by allowing explicit template syntax and requires clauses, providing stricter type constraints and easier access to the deduced types.
#include <vector>
#include <concepts>

// C++14 Generic Lambda
auto generic = [](auto x, auto y) { 
    return x + y; 
};

// C++14 Compiler Equivalent
class __Generic_Lambda {
public:
    template <typename T1, typename T2>
    inline auto operator()(T1 x, T2 y) const {
        return x + y;
    }
};

// C++20 Templated Lambda with requires clause
auto templated = []<typename T> requires std::integral<T> (std::vector<T>& vec, T value) {
    vec.push_back(value);
};
Master C++ with Deep Grasping Methodology!Learn More