mutable关键字强制解除按值捕获变量的const限定,使其可在lambda体内修改;不加则编译报错,且不影响生命周期、捕获方式或线程安全。

mutable lambda 能改 value-captured 变量,但默认不能
按值捕获的变量(比如 [x]{ return x; })在 lambda 体内是只读的——哪怕你没写 const,编译器也悄悄加了。想改它?必须显式加 mutable 关键字。这不是可选优化,是语言硬性要求。
常见错误现象:error: assignment of read-only variable 'x',尤其当你在 lambda 里写 x++ 或 x = 42 却忘了 mutable 时。
-
[x]() mutable { x++; return x; }合法;[x]() { x++; return x; }编译失败 - 捕获列表里有多个变量,
mutable影响全部:即使只改其中一个,所有按值捕获的变量都变成可写 - 按引用捕获(
[&x])不受mutable控制——它本来就能改,加不加都一样
什么时候非用 mutable 不可
典型场景是带状态的闭包:计数器、缓存、状态机跳转。这些逻辑依赖 lambda 自身修改捕获的副本,而不是靠外部变量或全局状态。
比如实现一个“只执行一次”的包装器:
立即学习“C++免费学习笔记(深入)”;
int x = 10;
auto once = [x]() mutable {
static bool called = false;
if (!called) {
called = true;
return x * 2;
}
return 0;
};注意:这里 x 是按值捕获,但真正需要 mutable 的其实是 called ——不对,等等,called 是 static 局部变量,和捕获无关。真正需要 mutable 的例子是:
int x = 0;
auto counter = [x]() mutable -> int {
return ++x; // 修改的是副本 x,不是原 x
};- 每次调用
counter()都返回递增的值(1, 2, 3…),因为x是副本且被标记为可变 - 原始
x始终不变,证明这是独立副本 - 如果去掉
mutable,++x直接报错
mutable 不影响生命周期,也不改变捕获方式
有人误以为 mutable 让变量“活得更久”或者“变成引用”,其实完全不是。它只解除对按值捕获变量的 const 限定,仅此而已。
- 按值捕获的变量仍随 lambda 对象销毁而销毁;
mutable不延长其生命周期 - 不会把
[x]变成[&x];引用捕获要显式写& - 性能上无额外开销:只是编译期去掉 const 修饰,生成的代码和手写 struct 成员可写一样
- C++11 起支持,所有主流编译器(GCC/Clang/MSVC)行为一致,无兼容性问题
容易被忽略的坑:mutable 和 const 成员函数冲突
当 lambda 作为参数传给期望 const 函数对象的接口时(比如某些 STL 算法内部调用 operator() 并假设它是 const),加上 mutable 就可能编译失败。
例如:
std::vector<int> v = {1,2,3};
int sum = 0;
std::for_each(v.begin(), v.end(), [sum](int x) mutable { sum += x; }); // ❌ 错!sum 没被修改到外面更糟的是,上面代码根本达不到累加目的——sum 是副本,改了也没用。但真正容易踩的坑是:
- 把
mutablelambda 传给要求const operator()的模板(如旧版std::bind某些用法) - 误以为
mutable能让 lambda 修改外部变量:它只能改自己的副本,想改外部得用引用捕获[&x] - 在多线程里滥用:多个线程同时调用同一个
mutablelambda 实例,会竞争修改它的内部副本——这不是线程安全的,除非你手动加锁
最常被忽略的一点:lambda 的可变性是绑定在类型上的。同一个捕获列表,加不加 mutable 就是两个完全不同的类型,不能互相赋值或混用。










