可调用对象(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,对应尾置声明 -> intint operator(int param1, int param2){ parameter_list: (int param1, int param2)}private:// 值传递的成员,注意是const,即类内部是不能改变成员的值。// 注:const成员必须在初始化列表初始化const int v_a;const int v_b;// 显式声明了mutableint 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了iostreamreturn v1; // 错误,默认机制下,被认为是void,应该加上尾置声明-> size_t。return []{return 42}; // 错误:返回lambda不能有v2引用传递。};v1 = 0;auto j1 = f(); // j1 = 2auto j2 = v2; // j2 = 3}
