一、引入
在 C++ 中,我們通常使用函數來完成特定的功能。但有時候,我們需要在一個函數內部定義一個小型的功能塊,這時如果單獨寫一個函數會顯得繁瑣。C++11 引入了?Lambda 函數,它是一種匿名函數,可以在需要的地方直接定義和使用,非常適合用于簡化代碼。
二、Lambda函數的基本用法
1.語法模板(看不懂沒關系,看完實例就理解了)
[捕獲列表](參數列表) -> 返回類型 {函數體
}
逐部分講解:
1.捕獲列表 []:
用于捕獲外部變量,決定 Lambda 函數如何訪問外部作用域的變量。
常見形式:
[]:不捕獲任何變量。
[=]:以值的方式捕獲所有外部變量。
[&]:以引用的方式捕獲所有外部變量。
[x, &y]:以值捕獲 x,以引用捕獲 y。
2.參數列表 ():
和普通函數的參數列表一樣,用于傳遞參數。
3.返回類型 -> 返回類型:
可以省略,編譯器會自動推導返回類型。
如果函數體中有多個返回語句,且類型不一致,則需要顯式指定返回類型。
4.函數體 {}:
實現具體的功能邏輯。
示例:
auto add = [](int a, int b) -> int {return a + b;
};
std::cout << add(2, 3); // 輸出: 5
三、?Lambda 函數的捕獲列表詳解
示例 1:不捕獲任何變量
int x = 10;
auto func = []() {// x 不可訪問,編譯錯誤// std::cout << x;
};
示例 2:以值的方式捕獲變量
int x = 10;
auto func = [x]() {std::cout << x; // 輸出: 10
};
x = 20;
func(); // 輸出仍然是 10,因為捕獲的是值
此時函數捕獲的是x的數值:10,而不是x這個變量,因此外界對x更改不會影響函數中10這個數
示例 3:以引用的方式捕獲變量
int x = 10;
auto func = [&x]() {std::cout << x; // 輸出: 10
};
x = 20;
func(); // 輸出: 20,因為捕獲的是引用
此時函數捕獲的是x這個變量,區別于上面那種
示例 4:混合捕獲
int x = 10, y = 20;
auto func = [x, &y]() {std::cout << x << " " << y; // 輸出: 10 20y = 30; // 修改 y 的值
};
func();
std::cout << y; // 輸出: 30
四、Lambda 函數的實際應用
示例 :STL 算法中的 Lambda 函數
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {3, 1, 4, 1, 5, 9};std::sort(nums.begin(), nums.end(), [](int a, int b) {return a > b; // 降序排序});for (int num : nums) {std::cout << num << " "; // 輸出: 9 5 4 3 1 1}return 0;
}
五、總結與注意事項
總結:
-
Lambda 函數是一種匿名函數,適合用于簡化代碼和實現小型功能。
-
捕獲列表決定了 Lambda 函數如何訪問外部變量。
-
Lambda 函數常用于 STL 算法和回調函數中。
注意事項:
-
捕獲列表的使用需要謹慎,避免不必要的值拷貝或引用捕獲。
-
如果 Lambda 函數的邏輯較復雜,建議單獨寫一個普通函數。
六、易混辨析與深入理解
1.[ ]和 < >的大亂斗
(1)兩者區別
特性 | [] (捕獲列表) | <> (模板參數列表) |
---|---|---|
用途 | 用于 Lambda 函數,定義如何捕獲外部變量 | 用于模板,定義類型參數 |
位置 | 出現在 Lambda 函數的開頭 | 出現在模板類或模板函數的定義或調用處 |
內容 | 捕獲方式(值、引用等) | 類型參數(如?int 等) |
它們的作用域和語法規則是獨立的,沒有任何關聯。
(2)如何選取
int x = 10;
auto func = [x](int y) -> int {return x + y; // 捕獲 x,參數 y
};
std::cout << func(5); // 輸出: 15
例如上面的程序,為什么不寫成下面的:
int x = 10;
auto func = [](int x,int y) -> int {return x + y;
};
std::cout << func(x,5); // 輸出: 15
分析解答:
特性 | 直接傳遞參數 | 使用捕獲列表 |
---|---|---|
依賴外部變量 | 不依賴,x ?通過參數傳遞 | 依賴,x ?通過捕獲列表捕獲 |
參數傳遞 | 每次調用都需要顯式傳遞?x | x ?在 Lambda 函數內部直接可用 |
性能 | 可能產生額外的拷貝開銷 | 引用捕獲可以避免拷貝開銷 |
代碼可讀性 | 更直觀,邏輯清晰 | 可能降低可讀性,尤其是捕獲列表復雜時 |
適用場景 | 適合簡單的、獨立的功能 | 適合需要封裝外部狀態的場景 |
?(3)為什么選擇捕獲列表?
場景 1:封裝外部狀態
int x = 10;
auto func = [x]() {std::cout << "Captured x: " << x;
};
x = 20; // 修改外部 x
func(); // 輸出: Captured x: 10
場景 2:避免重復傳遞參數
int x = 10;
auto func = [x](int y) {return x + y; // 不需要每次調用都傳遞 x
};
std::cout << func(5); // 輸出: 15
std::cout << func(10); // 輸出: 20
場景 3:引用捕獲避免拷貝
std::vector<int> data = {1, 2, 3};
auto func = [&data]() {data.push_back(4); // 修改外部 data
};
func();
std::cout << data.size(); // 輸出: 4
2.[&]
?和?[&x]的區別
[&]
:以引用的方式捕獲所有外部變量
作用:捕獲所有外部變量,并以引用的方式訪問它們。
-
特點:
-
簡潔,不需要顯式列出所有變量。
-
適合需要捕獲多個變量的場景。
-
可讀性較差,因為不清楚具體捕獲了哪些變量。
-
[&x]
:以引用的方式捕獲特定變量
作用:只捕獲指定的變量?x
,并以引用的方式訪問它。
-
特點:
-
明確,清楚地知道捕獲了哪個變量。
-
適合只需要捕獲少量變量的場景。
-
可讀性較好,但可能會顯得冗長。
-