lambda表达式简介
lambda表达式是C++11新特性(C++11 特性),用于创建一个可调用单元,可理解成匿名内联函数。
可调用单元 是指什么?
可调用单元通常是指可调用对象(或函数)。
可调用对象
一个对象或表达式,如果可对其使用可调用运算符("()"),则称这个对象或表达式为可调用对象。
可调用对象组成
4种:函数, 函数指针, 重载了"()"的类,lambda表达式。
本文主要介绍lambda表达式。
[======]
lambda表达式语法
格式
[捕获列表](参数列表) mutable或exception -> 返回值类型 { 函数体 }
捕获列表
捕获列表可以为空,但"[]"不可用省略,用来标识一个lambda表达式开始。
lambda要访问所在函数的局部变量时,必须先捕获。2种局部变量捕获方式:
- 值捕获:创建lambda表达式时,拷贝所在函数的局部变量的值。
- 引用捕获:创建lambda表达式时,获取变量的引用。需要确保lambda使用变量时,变量对应对象存在。
注意:
1)值捕获是发生在lambda创建时,而非调用时。
2)lambda无法捕获任何static变量、全局变量,不过可以在lambda表达式内部直接访问。
// 值捕获拷贝变量,发生在lambda创建时
void func1()
{
size_t v1 = 10;
auto a = [v1]()->int { return v1; }; // 值捕获v1 = 10
v1 = 0;
auto b = a(); // lambda调用时,不会改变捕获值,因此b为10
}
// 引用捕获获取变量引用
void func2()
{
size_t v1 = 10;
auto a = [&v1]()->int { return v1; }; // 引用捕获v1
v1 = 0;
auto b = a(); // b为0
}
// lambda访问全局变量、static变量
size_t sz = 10;
static int counter = 0;
void func3()
{
auto f0 = [sz](int a, int b) { return a > b}; // 错误:lambda无法捕获任何具有静态存储持续时间的变量
auto f = [](string& x, string& y)->bool {
counter++; // OK:lambda表达式可以直接访问static变量
if (x.size() > sz) { // OK:lambda表达式可以直接访问global变量
return true;
}
return x > y;
};
string s1 = "123456";
string s2 = "abcdefgh";
auto r = f(s1, s2);
cout << r << endl; // 打印0
cout << counter << endl; // 打印1
}
隐式捕获
捕获列表中写“&”,告诉编译器采用引用捕获方式;写“=”,告诉编译器采用值捕获方式。如果有个别局部变量不需要用统一的捕获方式,可以专门指出其捕获方式,然后用逗号方式分隔不同捕获。
string v1 = "abc";
string v2 = "123465";
string v3 = "qwert";
auto f1 = [&](const string& s)->{ cout << v1.size() + s.size() << endl; }; // 引用捕获
auto f2 = [=](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕获
auto f3 = [=, &v1](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 引用捕获v1, 其余都是值捕获
auto f4 = [&, v2, v3](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕获v2, v3, 其余都是引用捕获
f1("test1");
f2("test2");
f3("test3");
参数列表
参数列表 可以为空,但"()"不可用省略。类似于函数定义。
mutable关键字
-
mutable对于const函数作用
const成员函数中,通常不能修改non-static数据成员。如果要修改,需要将数据成员声明为mutable,表示该变量可变,不再有constness(常量性)约束。
具体可参见Effective C++ 条款03:尽可能使用const。 -
mutable对于lambda作用
在lambda表达式中,mutable有类似效果,默认不能修改值捕获的变量。
当lambda要修改值捕获的变量时,必须添加mutable声明。
void fcn()
{
size_t v1 = 10;
auto f = [v1]() mutable { return ++v1; }; // 加上mutable才能修改值捕获的v1
// <=> auto f = [v1]() mutable -> int { return ++v1; };
v1 = 0;
auto j = f(); // j 为11
}
指定lambda返回类型
默认情况下,
1)如果lambda函数体只包含单一return语句,可以省略lambda返回类型(编译器自动推断返回类型)。
2)如果包含return之外的任何语句,编译器假定此lambda返回void。
简单来说,如果编译器无法推断lambda返回类型,就需要尾置返回类型,不可省略。
例,
vector<int> vec;
... // vec插入数据
// OK, 单一return语句, 编译器能推断出lambda的返回类型
transform(vec.begin(), vec.end(), vec.begin(), [](int i) { return i < 0 ? -i : i; });
// 错误, 不能推断lambda的返回类型
transform(vec.begin(), vec.end(), vec.begin(), [](int i) -> int {
/* 非单一return语句 */
if (i < 0) return -i;
else return i;
});