lambda 表达式与mutable
目录
- 1. mutable 关键字表示lambda表达式可以修改捕获的变量
- 2. mutable 与 捕获引用的区别: mutable 复制了外部变量,然后对其修改。 捕获引用:直接修改外部变量
- 3. lambda 表达式底层实现原理
- 3.1 Functor func 这个调用的哪个构造器呢
1. mutable 关键字表示lambda表达式可以修改捕获的变量
你给出的代码片段展示了C++11 lambda表达式的使用,其中包含了一个捕获列表和一个函数体。让我们逐行解释一下:
int id = 0;
auto func = [id]()mutable {
std::cout << "id:" << id << std::endl;
++id; // 对的,可以修改
};
1. int id = 0;:定义一个整型变量id并初始化为0。
2. auto func = [id]()mutable { ... };:
* auto关键字表示函数的返回类型将由编译器自动推断。
* [id]是捕获列表,它告诉编译器这个lambda表达式捕获了外部变量id。
* ()表示这是一个无参数的函数。
* mutable关键字表明这个lambda表达式可以修改捕获的变量。这在需要修改捕获变量的情况下非常有用。
3. std::cout << "id:" << id << std::endl;:在控制台输出id:和当前的id值。
4. ++id;:对id进行前置递增操作。
这个lambda表达式创建了一个可以访问和修改id变量的函数对象。每次调用这个函数时,它会输出当前的id值,并将id递增1。由于使用了mutable关键字,lambda表达式可以修改捕获的变量id。
"mutable"这个词在英语中意味着“可变的”。在C++中,mutable关键字用于修饰捕获的变量,使得lambda表达式或函数可以修改这些变量的值。通常情况下,lambda表达式捕获的变量是不可变的,使用mutable关键字可以破坏这种不可变性,允许对捕获变量进行修改。
例如:
int x = 0;
auto lambda = [x]() mutable {
x++;
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出 "x: 1"
lambda(); // 输出 "x: 2"
在这个例子中,x是被捕获的变量,mutable关键字使得lambda表达式可以修改x的值。每次调用lambda函数时,x的值都会增加1。
2. mutable 与 捕获引用的区别: mutable 复制了外部变量,然后对其修改。 捕获引用:直接修改外部变量
在C++中,mutable关键字和引用捕获方式(&)有不同的用途和效果,它们可以在某些情况下结合使用,但它们的基本功能和影响是不同的。下面详细解释一下它们之间的区别:
- MUTABLE 关键字
mutable关键字用于修饰捕获的变量,使得lambda表达式或函数可以修改这些变量的值。它主要用于捕获的常量引用或常量值的情况。使用mutable关键字可以让lambda表达式或函数修改捕获变量的值。
int x = 0;
auto lambda = [x]() mutable {
x++;
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出 "x: 1"
lambda(); // 输出 "x: 2"
在这个例子中,x是被捕获的变量,mutable关键字使得lambda表达式可以修改x的值。
- 引用捕获(&)
引用捕获方式允许lambda表达式或函数直接修改外部变量的值,而不需要创建变量的副本。这在需要修改外部变量时非常有用,尤其是当外部变量的类型是昂贵或不可变的对象时。
int x = 0;
auto lambda = [&x]() {
x++;
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出 "x: 1"
lambda(); // 输出 "x: 2"
在这个例子中,x是通过引用捕获的,这意味着lambda表达式直接修改了外部变量x的值。
结合使用 MUTABLE 和引用捕获
你可以在同一个lambda表达式中结合使用mutable关键字和引用捕获。例如:
int x = 0;
auto lambda = [&x]() mutable {
x++;
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出 "x: 1"
lambda(); // 输出 "x: 2"
在这个例子中,x既是通过引用捕获的,也是mutable关键字作用的对象。这意味着lambda表达式可以修改x的值,同时也可以修改捕获变量本身(如果有的话)。
总结
- mutable关键字用于修改捕获的变量值。
- 引用捕获(&)用于直接修改外部变量的值。
- 它们可以在某些情况下结合使用,但它们的基本功能和影响是不同的。
3. lambda 表达式底层实现原理
你给出的代码展示了如何使用C++中的结构体(struct)来创建一个函数对象(Functor),这个函数对象捕获并修改了外部变量id。下面是对代码的逐行解释:
struct Functor {
public:
void operator()() {
std::cout << "id:" << id << std::endl;
++id; // 对的,可以修改
}
private:
int id; // 拷贝了外部的id变量值
};
Functor func;
1. struct Functor { ... }:定义了一个名为Functor的结构体。
2. void operator()():这是一个函数对象的函数调用运算符,它定义了如何调用Functor对象。每次调用func()时,都会执行这个函数体。
3. std::cout << "id:" << id << std::endl;:在控制台输出id:和当前的id值。
4. ++id;:对id进行前置递增操作。
5. private: int id;:id是Functor结构体的一个私有成员变量,它被初始化为外部变量id的值。每个Functor对象都有自己独立的id副本,这意味着修改id不会影响外部的id变量,除非显式地传递外部变量的引用或指针。
6. Functor func;:创建了一个Functor对象func。这个对象拥有自己的id副本,初始值为外部变量id的值。
使用外部变量
如果你希望Functor对象能够修改外部变量id,你需要将外部变量传递给Functor对象,通常通过引用或指针。例如:
int id = 0;
Functor func;
void callFunctor() {
func(id); // 传递外部变量id的引用
}
void someFunction() {
callFunctor();
// id 现在是 1
}
在这个例子中,func(id)将外部变量id的引用传递给Functor对象func,这样func就可以直接修改id的值。
3.1 Functor func 这个调用的哪个构造器呢
在C++中,结构体(struct)和类(class)都有默认的构造器,它们会初始化所有的成员变量。对于你给出的代码:
struct Functor {
public:
void operator()() {
std::cout << "id:" << id << std::endl;
++id;
}
private:
int id;
};
Functor func;
当你创建Functor对象func时,id成员变量会被默认初始化为0。因此,Functor func;这一行代码实际上调用的是Functor构造器,它初始化id成员变量为0。
默认构造器
对于Functor结构体,C++会自动为它生成一个默认构造器,该构造器不接受任何参数,并且会初始化所有未显式初始化的成员变量。因此,Functor func;调用的是这个默认构造器,它会将id成员变量初始化为0。
显式构造器
如果你希望显式地定义构造器,可以这样做:
struct Functor {
public:
Functor(int _id) : id(_id) {} // 显式构造器
void operator()() {
std::cout << "id:" << id << std::endl;
++id;
}
private:
int id;
};
Functor func(10); // 调用显式构造器,初始化id为10
在这个例子中,我们定义了一个显式的构造器Functor(int _id) : id(_id) {},它接受一个int参数并将其赋值给id成员变量。然后我们使用Functor func(10);调用这个构造器,初始化id为10。