在 C++11 及之后的版本中,C++ 提供了 lambda 表達(dá)式,它是一種方便了參數(shù)傳遞和定義匿名函數(shù)的方法。該方法通常用于封裝算法、執(zhí)行異步方法 ,也就是說比較適用于少量的代碼。以下原文:
In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it is invoked or passed as an argument to a function. Typically lambdas are used to encapsulate a few lines of code that are passed to algorithms or asynchronous methods. This article defines what lambdas are, compares them to other programming techniques, describes their advantages, and provides a basic example.
1. Lambda 表達(dá)式的組成
先對(duì) lambda 表達(dá)式有一個(gè)直觀的認(rèn)識(shí),參考下面程序,該程序完成的是將輸入的數(shù)組 nums 按照絕對(duì)值大小進(jìn)行升序排列。
int main() {
std::vector<int> nums = {1, 5, 3, 4, 2, -1, 10};
std::sort(nums.begin(), nums.end(), [](int a, int b) mutable throw() -> bool {
// lambda 表達(dá)式函數(shù)體,在這里做到了將輸入數(shù)組升序排列
return (std::abs(a) < std::abs(b));
});
for (int i : nums) std::cout << i << " ";
// >: 1 -1 2 3 4 5 10
}
拋開邊邊角角,單獨(dú)拿出最重要的一部分來學(xué)習(xí),[](int a, int b) mutable throw() -> bool{ // statement } 就是 lambda 表達(dá)式最原始的內(nèi)容。在該表達(dá)式中,每一部分的含義如下敘述:
-
[]捕獲字句:用來捕獲周圍范圍中出現(xiàn)的變量,也被稱為引導(dǎo)子句,可以在其中聲明獲取的變量是值還是引用,默認(rèn)值為&,上文中的例子和[&]是一樣的效果,具體例子見下文。 -
()參數(shù)列表:用來獲取參數(shù),對(duì)于一個(gè)一般的lambda函數(shù),使用起來和一般的指針函數(shù)沒有區(qū)別,也是需要有參數(shù)列表的,具體例子見下文。 -
mutable可變類型(可選):一般來說,在lambda體中調(diào)用運(yùn)算符的變量,都是以const value來使用的,加上這個(gè)mutable之后,人家變成了變量來使用,具體栗子見下文。 -
throw()異常類型(可選):和普通函數(shù)一樣樣,lambda函數(shù)也可能引發(fā)異常,如果不會(huì)引發(fā)異常的話,直接聲明noexcept就可以啦~ -
-> bool返回類型(可選):繼續(xù)和普通函數(shù)一樣 -
{// statement }lambda體:和一般的函數(shù)體一樣。
不難發(fā)現(xiàn),lambda 函數(shù)和一般的函數(shù)沒有太大區(qū)別,基本上只有在頭部位置有特殊語(yǔ)法。
2. 捕獲語(yǔ)句的使用 & 可變規(guī)范 mutable
拿出栗子:
int main() {
int num = 1; // 在上文中聲明好變量 num
auto f = [n = num]() { // 在下文中通過 捕獲[] 來獲取 num,并在 lambda 函數(shù)體中進(jìn)行使用
std::cout << n << std::endl;
// std::cout << ++num << std::endl; // 錯(cuò)誤的使用,因?yàn)?num 是不可變的常量
};
f(); // >: 1
auto m = [num]() mutable {
std::cout << ++num << std::endl; // 將內(nèi)部變量聲明成 mutable 可變類型,此時(shí)可以修改內(nèi)部變量
};
m(); // >: 2
std::cout << num << std::endl; // >: 2
}
mutable 可變聲明優(yōu)勢(shì)在于可以在內(nèi)部直接改變外部變量,相當(dāng)于使用了 [&num] 引用方法。
3. 參數(shù)列表
再拿出一個(gè)栗子:
int main() {
auto y = [](int a, int b) {
return a + b;
};
std::cout << y(3, 2); // >: 5
}
從這里開始,也就是參數(shù)列表開始,后面的內(nèi)容都是可選項(xiàng),也就是如果為空,那么就直接省略不寫即可。例如:
int main() {
auto empty = [] {
std::cout << "Wow!空的~" << std::endl;
// 啥也沒有只有個(gè)函數(shù)體};
};
empty(); // >: Wow!空的~
}
4. 特殊用法
4.1 花里胡哨的 lambda 嵌套
int main() {
// 兩層 lambda 嵌套,看起來挺花里胡哨
auto embed_embed_lambda = [](int a) {
std::cout << a << " - - ";
return [](int c) { return c / 2; };
};
std::cout << embed_embed_lambda(2)(2) << std::endl; // >: 2 - - 1
}
4.2 高階 lambda 函數(shù)
高階函數(shù)是指,采用另一個(gè) lambda 表達(dá)式作為其參數(shù)或返回 lambda 表達(dá)式的 lambda 表達(dá)式(不知不覺想起了俄羅斯套娃??)
int main() {
// 返回 function 對(duì)象的 lambda 表達(dá)式
auto get_function = [](int x) -> std::function<int(int)> {
return [=](int y) { return x + y; };
};
// 使用 function 為對(duì)象作為其參數(shù)的 lambda 表達(dá)式
auto param_function = [](const std::function<int(int)> &f, int n) {
return f(n) * 2;
};
auto ans = param_function(get_function(2), 3); // x = 2, n = 3
std::cout << ans << std::endl;
}
5. 總結(jié)
寫到此處,關(guān)于 C++ 的 lambda 語(yǔ)法規(guī)范和用法已經(jīng)學(xué)習(xí)了一小部分,它作為一種方便靈活的方法隨用隨學(xué)也是闊以的。
因?yàn)閰?shù)類型和函數(shù)模板參數(shù)一樣可以被推導(dǎo)而無需和具體參數(shù)類型耦合,有利于重構(gòu)代碼;和使用auto聲明變量的作用類似,它也允許避免書寫過于復(fù)雜的參數(shù)類型。特別地,不需要顯式指出參數(shù)類型使得使用高階函數(shù)變得更加容易。
以下程序源碼:
/**
* Created by Xiaozhong on 2020/8/30.
* Copyright (c) 2020/8/30 Xiaozhong. All rights reserved.
*/
#include <functional>
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {1, 5, 3, 4, 2, -1, 10};
std::sort(nums.begin(), nums.end(), [&](int a, int b) mutable throw() -> bool {
// lambda 表達(dá)式函數(shù)體,在這里做到了將輸入數(shù)組升序排列
return (std::abs(a) < std::abs(b));
});
for (int i : nums) std::cout << i << " ";
// >: 1 -1 2 3 4 5 10
int num = 1; // 在上文中聲明好變量 num
auto f = [n = num]() { // 在下文中通過 捕獲[] 來獲取 num,并在 lambda 函數(shù)體中進(jìn)行使用
std::cout << n << std::endl;
// std::cout << ++num << std::endl; // 錯(cuò)誤的使用,因?yàn)?num 是不可變的常量
};
f(); // >: 1
auto m = [num]() mutable {
std::cout << ++num << std::endl; // 將內(nèi)部變量聲明成 mutable 可變類型,此時(shí)可以修改內(nèi)部變量
};
m(); // >: 2
std::cout << num << std::endl; // >: 2
auto y = [](int a, int b) {
return a + b;
};
std::cout << y(3, 2); // >: 5
auto empty = [] {
std::cout << "Wow!空的~" << std::endl;
// 啥也沒有只有個(gè)函數(shù)體};
};
empty(); // >: Wow!空的~
// 聲明一個(gè)函數(shù),然后直接使用 (5, 3)
int n = [](int a, int b) { return a + b; }(5, 3);
std::cout << n << std::endl; // >: 8
// 兩層 lambda 嵌套,看起來挺花里胡哨
auto embed_embed_lambda = [](int a) {
std::cout << a << " - - ";
return [](int c) { return c / 2; };
};
std::cout << embed_embed_lambda(2)(2) << std::endl; // >: 2 - - 1
// 返回 function 對(duì)象的 lambda 表達(dá)式
auto get_function = [](int x) -> std::function<int(int)> {
return [=](int y) { return x + y; };
};
// 使用 function 為對(duì)象作為其參數(shù)的 lambda 表達(dá)式
auto param_function = [](const std::function<int(int)> &f, int n) {
return f(n) * 2;
};
auto ans = param_function(get_function(2), 3);
std::cout << ans << std::endl;
}
