C++八股题整理 - lambda表达式
lambda表达式(匿名函数)
lambda表达式的定义?
[capture](parameters) -> return_type { function_body };
// e.g. [](int a, int b)->int { return a+b; };
// e.g. []{ return 666; };
// e.g. [a](int b) { return a+b; };
- [capture] 捕获列表:lambda表达式所在函数中定义的局部变量的列表,不可省略
- 分为值捕获和引用捕获
- [ ]:默认不捕获任何列表
- [=]:默认以值捕获所有变量
- [&]:默认以引用捕获所有变量
- [=, &x]:默认以值捕获所有变量,但是x是例外,通过引⽤捕获
- [&, x]:默认以引⽤捕获所有变量,但是x是例外,通过值捕获
- (parameters) 参数列表:和普通函数的参数列表相同。如果不需要参数,可以省略
- return_type 返回类型:指定返回类型。可以省略,省略时编译器推断返回类型
- { function_body } 函数体:实际执行的代码,只能使用捕获列表和参数列表中的变量,不可省略
lambda表达式的使用场景?
// 使用场景1:内嵌于函数体
void func(){
auto f = [](int a, int b)->int { return a+b; };
cout << f(1,2) << endl; // 3
}
// 使用场景2:内嵌于函数的参数列表
int val = 3;
vector<int> v {1, 8, 5, 3, 6, 10};
int count = std::count_if(v.beigin(), v.end(), [val](int x) { return x > val; }); // v中大于3的元素数量
lambda表达式的优缺点?
- 优点
- 可以在函数内部定义lambda表达式,因此其定义和使用是在同一个地方进行的,不像函数一样定义和使用间隔很远
- lambda表达式可以理解为一个匿名的内联函数,它相比非内联的函数,能避免函数调用时的栈操作,因此提高效率
- 缺点
- 难以调试,由于 Lambda 表达式在底层被转换为匿名类,编译器生成的错误信息有时可能不太直观
lambda表达式的底层实现?
编译器实现 lambda 表达式大致分为一下几个步骤:
- 创建 lambda匿名类,实现构造函数,使用 lambda 表达式的函数体重载operator()(所以 lambda 表达式 也叫匿名函数对象)
- 创建 lambda 对象
- 通过对象调用 operator()
// lambda表达式
[a, &b](int c) { return a + b + c; }
// 翻译后的类代码
class Lambda_xxxx {
private:
int a; // 按值捕获的成员变量
int& b; // 按引用捕获的成员变量
public:
// 构造函数,用于初始化捕获的变量
Lambda(int a_, int& b_) : a(a_), b(b_) {}
// 函数调用运算符
int operator()(int c) const {
return a + b + c;
}
};
lambda表达式的内存大小?
- 捕获列表为空,lambda表达式等价于一个函数,内存大小通常为1
- 捕获列表非空,lambda表达式被翻译为匿名类的对象,内存大小与类对象的大小一致。注意,引用捕获的变量,存储的是它的地址,因此大小为一个指针的大小(32位系统4,64位系统8)
int a = 1;
auto f0 = [](){ return 0;};
auto f1 = [a](){ return a;};
auto f2 = [&a](){ return a;};
cout << sizeof(f0) << endl; // 1
cout << sizeof(f1) << endl; // 4 int的大小
cout << sizeof(f2) << endl; // 4或8 指针的大小
捕获的变量,是在什么时候赋值?
捕获的变量,是在lambda表达式创建时赋值,而不是在lambda表达式执行时赋值。
看lambda表达式的底层实现就能明白,lambda表达式创建对于类的构造函数,执行对于调用operator()。
int a=1;
auto f1 = [=](){ return a;};
auto f2 = [&](){ return a;};
a=2;
cout << f1() << endl; // 1
cout << f2() << endl; // 2
引用捕获的变量,要注意什么?
当以引用方式捕获一个变量时,要保证在lambda表达式执行时,变量的生命周期没有结束。
int *a = new int(1);
auto f = [&](){ return *a;};
delete a;
cout << f() << endl; // error