可调用对象(callable object),即可以対其使用()调用运算符。C++的可调用对象有:
- 函数
- 函数指针
- 函数对象(重载调用运算符)
- lambda表达式
一个lambda表达式表示一个可调用的代码单元,可以将其理解为一个未命名的内联函数(匿名函数),可以定义在函数内部。
编译阶段,编译器会根据lambda生成一个新的类类型。
auto lambda = [capture_list](parameter_list) mutable - > return_type
{
//function_body
}
//下面是对每个部分的详细介绍。
//capture_list: 捕获列表,花括号[]括起。
// 包含函数体使用到的“父函数变量”,即定义在lamba所在函数中的非static局部变量。
// 在“父函数”外部的变量,不需要包含。
// 可为空,表示没有使用到“父函数”的局部变量。
// 捕获方式: 局部变量是何种方式传递进来,值传递或引用传递。
// 值传递: [arg]形式,变量必须能拷贝,且在lambda创建时拷贝,而不是调用时。所以在创建lambda后,
// 外部修改变量不会影响lambda内对应的值。lambda内部也无法修改这些值,必须mutable声明
// 引用传递: [&arg]形式,和函数引用传递一样,要注意变量在调用时是否被失效。
// 隐式捕获: [=]或[&]形式,由编译器来推断我们使用到的局部变量是值传递或者引用传递。
// 混合捕获: [=,arg1,...,argn]形式,arg1~argn是引用传递,其他值传递。
// [&,arg1,...,argn]形式,arg1~argn是值传递,其他引用传递。
// [=,&]、[arg1,=]、[arg1,&]都是错误的
//parameter_list:形参列表,不能有默认实参
//function_body:函数体,可无需通过捕获列表访问static变量和“父函数”以外的名字。
//mutable: 可修改模式,捕获列表中值传递的变量,在lambda内部是无法被修改的,mutable之后,就能修改。
// 不过也只是修改lambda内部的副本,并不会影响外部的。
//return_type:返回类型,必须使用尾置返回。
// 为空则默认,默认机制如下:
// 如果函数体就是1个return语句,自动推断返回类型。
// 否则,返回类型是void
// 返回lambda,则该lambda的捕获不能有引用传递。
lambda本质上是一个函数对象,即一种类类型。
auto lambda = [capture_list](parameter_list) mutable - > return_type
{
//function_body
}
// 上下本质上是一样的
class lambda {
public:
lambda(int v_a, int v_b); // capture_list: [a, b]
lambda(int& r_a, int &r_b); // capture_list: [&a, &b]
// 返回类型int,对应尾置声明 -> int
int operator(int param1, int param2){ parameter_list: (int param1, int param2)
}
private:
// 值传递的成员,注意是const,即类内部是不能改变成员的值。
// 注:const成员必须在初始化列表初始化
const int v_a;
const int v_b;
// 显式声明了mutable
int v_a;
int v_b;
// 引用传递的成员
// 注:引用成员必须在初始化列表初始化,且不能有默认构造函数。
int& r_a;
int& r_b;
}
例子:
void fcn3()
{
size_t v1 = 1, v2 = 2, v3 = 3; // “父函数”局部变量
static fuck = 0;
auto f = [&v2, v1] // v2:引用传递,v1:值传递
(int a, int b = 1) // 错误:不能有默认实参
mutable // v1可自修改
{
++v3; // 错误:没有捕获。
++fuck; // 正确:可直接访问static变量
++v2; // 正确,只要v2不是const size_t类型。
++v1; // 正确,有mutable声明
cout << v1; // 正确,只要include了iostream
return v1; // 错误,默认机制下,被认为是void,应该加上尾置声明-> size_t。
return []{return 42}; // 错误:返回lambda不能有v2引用传递。
};
v1 = 0;
auto j1 = f(); // j1 = 2
auto j2 = v2; // j2 = 3
}