lambda表达式:[capture list] (params list) mutable exception-> return type { function body } 也叫匿名函数,以下统称为匿名函数。
对于一个函数而言,由4部分组成:
返回值类型 函数名(形参列表) { 函数体; }而针对匿名函数而言,因为没有名字,我们在定义时即使用,它由以下几部分组成:
[捕获列表] (形参列表) ->返回值类型 { 函数体; }其中,形参列表与返回值类型中还存在可选参数,我们后面在讨论。
可以看到匿名函数与普通函数没有太大区别,唯一的区别就是多了一个捕获列表。
通常匿名函数的形式为 [](){}的组合,而匿名函数的调用也与普通函数相似。普通函数的调用是通过 函数名() 的方式调用,而匿名函数没有函数名则是使用 匿名函数() 的方式调用的。
[](){}; // 匿名函数声明 [](){} (); // 匿名函数的调用 // 注:在使用匿名函数时主要有以上两种方式: // 第一种称之为匿名函数的声明,是在匿名函数作为参数时使用,类比普通函数的函数名 // 第二种称之为匿名函数的调用,是在直接调用匿名函数的方法。因为匿名函数的调用形式可以写成一行代码语句的形式,而在同一作用域下的局部变量是对所有语句都可见的,也就是说我们可以通过捕获列表来确定匿名函数调用时使用哪些外部变量。
捕获列表主要有以下几种形式:
捕获形式说明[]不捕获任何外部变量[a, b…]默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符)[this]以值的形式捕获this指针[=]以值的形式捕获所有外部变量(包括lambda所在类的this)[&]以引用形式捕获所有外部变量(同上)[=, &x]变量x以引用形式捕获,其余变量以传值形式捕获[&, x]变量x以值的形式捕获,其余变量以引用形式捕获按引用捕获相当于按引用传递。这里需要说明的是,按值捕获相当于实参传递形参的过程,捕获的变量在匿名函数内部生成一份等值的拷贝。
可选参数: mutable指示符:用来说用是否可以修改按值捕获的变量。
实例:按值捕获外部变量
int a = 10; [=]() { cout << a << endl; }(); // 捕获外部变量a [=]() mutable{ a = 20; cout << a << endl; }(); // 修改a cout << a << endl; // 输出 10 20 10以上代码通过 [=] 可以捕获外部变量,并将其输出。当我们想要修改通过按值捕获的变量时,需要加上mutable说明符。并且我们可以看到虽然在函数内部的 a 被修改成了 20,但是在匿名函数外的 a 的值没有被改变。
小结:按值捕获的变量默认是不可修改的,类似普通函数的const形参传递,而通过添加 mutable 说明符可以修改这些变量。不过,通过的按值捕获的变量是不会影响原来的外部变量的。
实例:按引用捕获外部变量
int a = 10; [&]() { cout << a << endl; }(); [&]() { a = 20; cout << a << endl; }(); [&]() mutable{ a = 30; cout << a << endl; }(); cout << a << endl; // 输出 10 20 30 30通过输出我们可以看到,1. mutable 说明符只针对按值捕获的外部变量,按引用捕获的外部变量可以直接修改。 2. 修改按引用捕获的外部变量,原来的外部变量也会改变。
可选参数: throw(类型) :表示匿名函数可以抛出指定类型的异常。
我们也可以使用 noexcept 异常规范来指示 lambda 表达式不会引发任何异常。
void MyFunction(int i) throw(); // 普通函数 [] () throw() {} (); // 匿名函数 void MyFunction(int i) noexcept; // 普通函数 []() noexcept {} (); // 匿名函数普通函数参数列表:
int fun(int a, int b); // 申明 fun(1,2); // 使用 // 注:fun()函数需要实现匿名函数参数列表:
[](int a,int b) {}; // 声明 [](int a, int b) {}(1, 2); // 使用可以通过 ->type 的方式指定返回值类型。
实例:返回值
auto ret = [](auto a, auto b) {return a + b; }(1, 1.5); cout << "ret = " << ret << endl; // 输出 2.5 // 声明返回类型为 int auto retInt = [](auto a, auto b) ->int{return a + b; }(1, 1.5); cout << "retInt = " << ret << endl; // 输出 2虽然匿名函数没有名字,但我们可以效仿函数指针的方式,使用指针去调用函数。由于匿名函数的类型比较复杂,这里我们可以使用auto自动类型推演来实现。
auto fp = [](int i) {cout << i << endl; }; fp(1); cout << typeid(fp).name() << endl; // class <lambda_b1aaf42c0977e0b63366721abda69325>同时,匿名函数可以将另一个 匿名函数作为其自变量。或者是两个匿名函数嵌套。
实例:匿名函数嵌套与调用
auto f = [](int x) {return x; }; // x cout << f(10) << endl; auto ff = [](int x) {return [x](int y) {return x * y; }; }; // x * y cout << ff(10)(20) << endl; auto fff = [](int x) {return [x](int y) {return [x,y](int z) {return x * y * z; }; }; }; // x * y * z cout << fff(10)(20)(30) << endl;实例:匿名函数嵌套 f(n) = n * (n-1)
auto fact = [](int n) {return [n](int x) {return n * x; }(n - 1); }; // n * (n-1) /* auto fact = [](int n) { return [n](int x) { return n * x; }(n - 1); }; */ cout << fact(1) << endl; // 0 cout << fact(2) << endl; // 2 cout << fact(3) << endl; // 6 cout << fact(4) << endl; // 12 cout << fact(5) << endl; // 20 cout << fact(6) << endl; // 30参考:C++11 中的std::function和std::bind 一文,我们可以将匿名函数的返回值声明为function<int(int)> 类型,这样就可以实现一个匿名函数作为参数被另一个匿名函数调用。
实例:
// 返回一个可调用对象(函数、函数对象、函数指针...) auto addTwoInt = [](int x) -> function<int(int)> { return [=](int y) { return x + y; }; }; auto higherorder = [](const function<int(int)>& f, int z) { return f(z) * 2; }; // Call the lambda expression that is bound to higherorder. auto answer = higherorder(addTwoInt(7), 8); // Print the result, which is (7+8)*2. cout << answer << endl; // 输出 30其中第一个函数 addTwoInt 所指向的函数接收两个参数,作用是对两个数求和。同时返回值类型是一个可调用对象,而 higherorder 所指向的函数接收的参数为一个 可调用对象和一个普通内置类型, 因此 higherorder 可以调用 addTwoInt 。
更多高阶 Lambda 表达式请参考 Microsoft C++ Lambda 表达式的示例
除此之外,匿名函数还可以用于一些C++ 的 STL 库中提供的一些函数,类似如
count_if:统计容器中符合某种条件的元素的个数。find_if :查找容器中第一个符合某种条件的元素。find_if_not():… 第一个不满足…等模板函数,都能使用匿名函数,并且使用起来也很方便。
实例:find_if()
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int min = 5; // 大于 min 的第一个数 auto iter1 = std::find_if(begin(arr), end(arr), [min](int n) { return n > min; }); if (iter1 != end(arr)) std::cout << "在 arr[] 中第一个大于 " << min << "的数为:arr[" << distance(begin(arr), iter1) << "]=" << *iter1 << endl; // 输出:在 arr[] 中第一个大于 5的数为:arr[5]=6 auto iter2 = find_if_not(begin(arr), end(arr), [min](int n) { return n > min; }); std::cout << "在 arr[] 中第一个不大于 " << min << "的数为:arr[" << distance(begin(arr), iter2) << "]=" << *iter2 << endl; // 输出:在 arr[] 中第一个不大于 5的数为:arr[0]=1