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 mutable lambda in C++ is an anonymous function object that allows the modification of variables captured by value. By default, the compiler-generated function call operator (operator()) of a lambda expression is const-qualified, making by-value captures read-only. Appending the mutable keyword removes this const qualification, enabling the lambda’s body to mutate its internal state (the copied variables) without affecting the original variables in the enclosing scope.

Syntax

The mutable specifier is placed after the parameter list and before the trailing return type (if specified) or the function body.
[capture_list](parameters) mutable -> return_type {
    // body
};
Parameter List Omission (C++23 vs. Older Standards): In C++11 through C++20, if the mutable specifier is used, the parameter list () is strictly mandatory, even if the lambda accepts no arguments. As of C++23 (via proposal P1102R2), the empty parameter list () is optional when using mutable or other specifiers.
int x = 0;
auto valid_cpp23 = [x] mutable { x++; };   // Valid in C++23; Syntax Error in C++11 through C++20
auto valid_all   = [x]() mutable { x++; }; // Valid in all standard versions

Under the Hood: The Closure Object

To understand mutable, you must examine the compiler-generated class (the closure type) created for the lambda.

Default Lambda (Non-Mutable)

When you capture a variable by value without mutable:
int x = 10;
auto standard_lambda = [x]() { 
    // x++; // ERROR: assignment of read-only variable 'x'
};
The compiler generates a class roughly equivalent to this:
class __Lambda_Default {
    int x; // Captured by value
public:
    __Lambda_Default(int _x) : x(_x) {}
    
    // operator() is implicitly const
    void operator()() const { 
        // x++; // Fails because 'this' is const
    }
};

Mutable Lambda

When you apply the mutable keyword:
int x = 10;
auto mutable_lambda = [x]() mutable { 
    x++; 
};
The compiler generates a class where the const qualifier is stripped from the operator():
class __Lambda_Mutable {
    int x; // Captured by value
public:
    __Lambda_Mutable(int _x) : x(_x) {}
    
    // operator() is NOT const
    void operator()() { 
        x++; // Succeeds. Modifies the closure's internal copy of 'x'
    }
};

Technical Characteristics and Edge Cases

  1. State Persistence: Because mutable modifies the member variables of the closure object itself, the mutated state persists across multiple invocations of the same lambda instance.
int val = 0;
auto counter = [val]() mutable { return ++val; };

counter(); // returns 1
counter(); // returns 2
  1. Isolation from Enclosing Scope: Mutating a by-value capture only alters the closure object’s internal copy. The original variable in the enclosing scope remains completely unchanged.
  2. Top-Level const Variables: If a variable is declared with a top-level const in the enclosing scope, capturing it by value copies the const qualifier directly to the closure’s internal data member. In this scenario, applying mutable strips the const from operator(), but the internal variable itself remains a const type and cannot be modified.
const int y = 10;
auto edge_case = [y]() mutable {
    // y++; // ERROR: 'y' is still a const int inside the closure
};
  1. Irrelevance to Reference Captures: The mutable keyword has no practical effect on variables captured by reference ([&], [&x]). A reference capture allows modification of the original variable regardless of whether the lambda is mutable, because the const qualification of the default operator() applies to the reference itself (which is inherently immutable in what it points to), not the referenced data.
Master C++ with Deep Grasping Methodology!Learn More