C++ lambda捕获类数据成员时是否需要标记为mutable?
C++类中Lambda无需mutable即可修改类成员的原理说明
普通场景下Lambda需要mutable的根本原因
默认情况下,C++ Lambda生成的闭包类的operator()是被const修饰的:
- 如果你按值捕获外部变量,相当于闭包类内部存储了该变量的副本作为普通成员
- 在
const修饰的成员函数中,无法修改类的普通成员,因此要修改值捕获的变量时,必须给Lambda加mutable关键字,去掉operator()的const属性。
类场景的特殊本质
你在类的非const成员函数中定义Lambda时,无论显式写[this]、还是用[=]/[&]隐式捕获,实际捕获的都是当前对象的this指针,而非直接捕获name、age这些成员变量,这是你观察到该现象的核心原因:
- 此时闭包类内部存储的成员是
Person* const this(即指针本身是常量,不能指向其他地址) - 默认const的
operator()只限制你不能修改捕获的this指针本身,并没有限制你通过this指针修改它指向的对象的非const成员 - 代码里的
name = new_name本质是this->name = new_name,完全符合const成员函数的调用规则,自然不需要加mutable。
补充验证点
你可以通过两个场景验证上述逻辑:
- 如果在类的const成员函数中定义该Lambda,此时捕获的this是
const Person*类型,你再尝试修改成员就会编译报错,因为指向的对象本身是const的,无法修改其成员 - 如果你用C++14及以上的初始化捕获直接捕获成员的副本,比如
[name = name],此时要修改这个捕获的name副本,依然需要给Lambda加mutable关键字。
你提供的测试代码如下:
#include <string> #include <iostream> class Person { private: std::string name{"None"}; int age{0}; public: Person() = default; Person(std::string name, int age) : name{name}, age{age} {} Person(const Person &p) = default; ~Person() = default; std::string get_name() const { return name; } void set_name(std::string name) {this->name = name; }; int get_age() const {return age; } void set_age(int age) {this->age = age; } auto change_person1(){return [this] (std::string new_name, int new_age) {name = new_name; age = new_age;};} auto change_person2() {return [=] (std::string new_name, int new_age) {name = new_name; age = new_age;};} auto change_person3() {return [&] (std::string new_name, int new_age) {name = new_name; age = new_age;};} }; int main() { Person person; auto p1 = person.change_person2(); p1("Hello", 12); std::cout << "Age is "<< person.get_age()<<"\n"; std::cout << person.get_name(); return 0; }
代码中change_person1/change_person2/change_person3的捕获方式本质都是捕获了当前对象的this指针,因此三个Lambda都可以正常修改类成员,运行后输出结果为:
Age is 12 Hello
内容的提问来源于stack exchange,提问作者Viren mahajan
相关产品推荐
相关产品推荐

