文法:
[捕獲](形參)限制(可選)->傳回值類型(可選){函數體}
限制包括 說明符如異常說明如
mutable
throw()
捕獲
捕獲形式
說明 | |
---|---|
[] | 不捕獲任何外部變量 |
[變量名 , …] | 預設以值的形式捕獲指定的多個外部變量并以逗号分隔 |
[this] | 以值的形式捕獲this指針 |
[=] | 以值的形式捕獲所有外部變量(需要拷貝) |
[&] | 以引用形式捕獲所有外部變量 |
[= , &x] | 變量x以引用形式捕獲,其餘變量以傳值形式捕獲 |
[& , x] | 變量x以值的形式不會,其餘變量以引用形式捕獲 |
捕獲示例
值捕獲
值捕獲和函數傳參中的值傳遞類似,被捕獲的變量的值在lambda表達式建立時通過值拷貝的方式傳入,故随後在外部對該變量的修改不會對lambda表達式中的值産生影響。
int a = 10, b = 100, c = 1000;
auto f = [a, b](){
cout << a << " " << b << endl;
cout << c << endl;//error 變量c沒有被捕獲 不能在lambda表達式中使用
};
f();//調用lambda表達式
但該方法在lambda表達式内部不能對拷貝的變量的值進行修改,即:
int a = 10, b = 100, c = 1000;
auto f = [a, b](){
a = 20;//error
cout << a << " " << b << endl;
};
f();
為了能在lambda表達式中修改捕獲的值的拷貝,需要加上關鍵字
mutable
,執行個體如下:
int a = 10, b = 100, c = 1000;
auto f = [a, b]()mutable{
a = 20;
cout << a << " " << b << endl;
};
f();
/**
輸出:
20 100
*/
引用捕獲
如果想在lambda表達式中改變外部變量的值,我們需要把變量的引用傳入,類似于函數傳參中的引用傳遞。
int a = 10;
auto f = [&a](){
cout << "改變前: " << a << endl;
a = 30;
cout <<"改變後:" << a << endl;
};
f();
a = 40;
f();
/**
輸出:
改變前: 10
改變後:30
改變前: 40
改變後:30
*/
隐式捕獲
上面的值捕獲和引用捕獲都需要我們在捕獲清單中顯示列出Lambda表達式中使用的外部變量。除此之外,我們還可以讓編譯器根據函數體中的代碼來推斷需要捕獲哪些變量,這種方式稱之為隐式捕獲。隐式捕獲有兩種方式,分别是[=]和[&]。[=]表示以值捕獲的方式捕獲所有外部變量,[&]表示以引用捕獲的方式捕獲所有外部變量。
隐式值捕獲
int a = 10, b = 100, c = 1000;
auto f = [=]() {
a = 20;//error
cout << a << " " << b << " " << c << endl;
};
f();
/**
輸出:10 100 1000
*/
想要修改捕獲到的值,同樣需要添加關鍵字
mutable
.
int a = 10, b = 100, c = 1000;
auto f = [=]()mutable {
a = 20;//correct
cout << a << " " << b << " " << c << endl;
};
f();
/**
輸出:20 100 1000
*/
隐式引用捕獲
int a = 10, b = 100, c = 1000;
auto f = [&](){
a = 20;//correct
cout << a << " " << b << " " << c << endl;
};
f();
/**
輸出:20 100 1000
*/
混合捕獲
在上述的例子中隻使用了單一的形式,lambda表達式支援混合的方式捕獲變量。執行個體:
int a = 10, b = 100, c = 1000;
auto f = [& , a](){
a = 20;//error
cout << a << " " << b << " " << c << endl;
};
f();
上述示例中,變量a以值的形式捕獲其餘變量以引用的形式捕獲,由于沒加
mutable
關鍵字故對a的拷貝進行修改非法。
int a = 10, b = 100, c = 1000;
auto f = [& , a]()mutable{
a = 20;//correct
cout << a << " " << b << " " << c << endl;
};
f();
/**
輸出:20 100 1000
*/
參數
lambda表達式的參數與普通函數的參數幾乎相同,僅有如下限制:
- 參數清單中不能有預設參數
- 不支援可變參數
- 所有參數必須有參數名
限制
說明符
mutable
,允許函數體修改各個捕獲對象的拷貝
constexpr
(
C++17
起),顯示指定函數調用符為
constexpr
,當函數體滿足
constexpr
函數要求時,即使未顯式指定,也會是
constexpr
。
異常說明
提供
throw
和
noexpect
字句
示例:
int a = 10, b = 100, c = 1000;
auto f = [&](int a, int b) mutable noexcept->int {
return a + b;
};
cout << f(a, b) + f(b, c) << endl;
/**
輸出:1210
*/
傳回值類型
此處可以省略,讓
auto
自動推導。
泛型lambda
從
C++14
開始,可以使用
auto x
作為參數。則可以寫出如下代碼:
auto f = [](auto x) {
cout << x << endl;
};
f(10);
f(10.11);
f("hello world!");
//輸出
10
10.11
hello world!
該特性的引入可以友善我們實作遞歸函數,相關示例将在後面提及。
生命周期
lambda表達式相關對象的生命周期如下:
- 全局,更外層作用域的生命周期不受影響。
- 使用值捕獲的情況,先于lambda表達式函數體構造對象,後于函數體執行完析構
- 在lambda表達式函數體内的對象,在函數體執行時建立,在閉包析構函數内析構
- lambda對象的生命周期為所在作用域結束,析構的順序為聲明的逆序析構
lambda表達式的實際使用
C++ 優先隊列
在
C++
優先隊列中的使用參考 priority_queue優先隊列
C++ 排序函數sort
#include<bits/stdc++.h>
using namespace std;
struct Point {
int x, y;
Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
};
int main() {
std::vector<Point> points;
for (int i = 0; i <= 10; ++i) {
points.push_back({ rand() % 10 , rand() % 10 });
}
for (auto [x, y] : points) cout << x << " " << y << endl;
cout << "After sort" << endl;
//寫法一
auto cmp = [&](const Point& a, const Point& b) {
//按照x為第一優先級 y為第二優先級從小到大排序
return a.x < b.x || (a.x == b.x && a.y < b.y);
};
std::sort(points.begin(), points.end(), cmp);
//寫法二
std::sort(points.begin(), points.end(), [&](const Point& a, const Point& b) {
//按照x為第一優先級 y為第二優先級從小到大排序
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
//上述兩種寫法等價
for (auto [x, y] : points) cout << x << " " << y << endl;
return 0;
}
1 7
4 0
9 4
8 8
2 4
5 5
1 1
5 2
7 6
1 4
After sort
遞歸函數
#include<bits/stdc++.h>
#include<functional>
using namespace std;
int main() {
//該部分的一些代碼來自cpp 參考手冊 lambda表達式部分 連結已經在文末給出
std::cout << "模仿遞歸 lambda 調用:\n斐波那契數:";
auto nth_fibonacci1 = [](int n) {
std::function<int(int, int, int)> fib = [&](int a, int b, int n) {
return n ? fib(a + b, a, n - 1) : b;
};
return fib(1, 0, n);
};
for (int i{ 1 }; i != 9; ++i) { std::cout << nth_fibonacci1(i) << ", "; }
//傳遞 lambda 給泛型
std::cout << "\n另一種 lambda 遞歸方案:\n斐波那契數:";
auto nth_fibonacci2 = [](int n) {
auto fib = [](auto self, int a, int b, int n) -> int {
return n ? self(self, a + b, a, n - 1) : b;
};
return fib(fib, 1, 0, n);
};
for (int i{ 1 }; i != 9; ++i) { std::cout << nth_fibonacci2(i) << ", "; }
//對于上述第二種遞歸方案也可以寫成如下形式
std::cout << "\n第二種 lambda 遞歸方案另一種形式:\n斐波那契數:";
auto nth_fibonacci = [](auto nth_fibonacci, int a, int b, int n)-> int {
return n ? nth_fibonacci(nth_fibonacci, a + b, a, n - 1) : b;
};
for (int i{ 1 }; i != 9; ++i) { std::cout << nth_fibonacci(nth_fibonacci , 1 , 0 , i) << ", "; }
return 0;
}
模仿遞歸 lambda 調用:
斐波那契數:1, 1, 2, 3, 5, 8, 13, 21,
另一種 lambda 遞歸方案:
第二種 lambda 遞歸方案另一種形式:
參考資料
C++ LAMBDA的演化
C++ lambda 分析
cpp 參考手冊 Lambda 表達式