C++ 中的匿名函數(Lambda 表達式)是 C++11 引入的一項重要特性,它允許你在需要的地方定義一個臨時的、無名的函數對象,使代碼更加簡潔和靈活。
1. 基本語法
Lambda 表達式的基本結構:
[capture list](parameter list) -> return type { function body }
[capture list]
:捕獲外部變量,指定如何將外部作用域的變量傳遞給 lambda。(parameter list)
:參數列表,與普通函數的參數類似(可省略,但若省略括號必須為空)。-> return type
:返回類型(可省略,編譯器會自動推導)。{ function body }
:函數體,包含具體的實現邏輯。
示例:
auto add = [](int a, int b) -> int { return a + b; };
int result = add(3, 4); // 結果為 7
2. 捕獲列表(Capture List)
捕獲列表用于訪問外部作用域中的變量,有以下幾種方式:
值捕獲(By Value)
- 使用
[var]
捕獲變量的副本。 - Lambda 創建時拷貝變量,后續修改不影響 lambda 內部的值。
int x = 10;
auto lambda = [x]() { return x * 2; }; // 捕獲 x 的值
x = 20;
std::cout << lambda(); // 輸出 20(捕獲的是 x 的副本)
引用捕獲(By Reference)
- 使用
[&var]
捕獲變量的引用。 - Lambda 內部使用的是變量的引用,外部修改會影響 lambda 內部。
int x = 10;
auto lambda = [&x]() { return x * 2; }; // 捕獲 x 的引用
x = 20;
std::cout << lambda(); // 輸出 40(引用 x 的當前值)
隱式捕獲
- 使用
[=]
捕獲所有外部變量的值(值捕獲)。 - 使用
[&]
捕獲所有外部變量的引用(引用捕獲)。
int a = 5, b = 10;
auto lambda = [=]() { return a + b; }; // 值捕獲 a 和 b
auto lambda2 = [&]() { a++; return a + b; }; // 引用捕獲 a 和 b
混合捕獲
- 同時使用值捕獲和引用捕獲,例如
[=, &a]
(默認值捕獲,a
引用捕獲)。
int a = 5, b = 10;
auto lambda = [=, &a]() { a++; return a + b; }; // a 引用捕獲,b 值捕獲
3. 參數列表
Lambda 的參數列表與普通函數類似,但不支持默認參數。
auto greet = [](const std::string& name) {std::cout << "Hello, " << name << "!" << std::endl;
};
greet("Alice"); // 輸出 "Hello, Alice!"
4. 返回類型
返回類型可省略,編譯器會自動推導。若需要顯式指定,使用 -> type
。
auto sum = [](int a, int b) -> int { return a + b; }; // 顯式指定返回類型
auto square = [](double x) { return x * x; }; // 自動推導返回類型
5. 可變 Lambda(Mutable Lambda)
默認情況下,值捕獲的變量在 lambda 內部是只讀的。使用 mutable
關鍵字可修改值捕獲的變量。
int x = 10;
auto lambda = [x]() mutable {x++; // 允許修改值捕獲的 xreturn x;
};
std::cout << lambda(); // 輸出 11(但外部 x 仍為 10)
6. 泛型 Lambda(C++14+)
使用 auto
作為參數類型,使 lambda 成為泛型函數。
auto print = [](const auto& value) {std::cout << value << std::endl;
};
print(42); // 輸出整數
print("test"); // 輸出字符串
7. 捕獲 this
指針
在類成員函數中,可捕獲 this
指針以訪問類的成員變量和方法。
class MyClass {
public:int value = 10;void func() {auto lambda = [this]() { return value * 2; };std::cout << lambda(); // 輸出 20}
};
8. 捕獲初始化(C++14+)
允許在捕獲列表中初始化新變量,可移動構造對象或重命名捕獲的變量。
int x = 10;
auto lambda = [y = x + 5]() { return y; }; // 初始化 y 為 15
std::cout << lambda(); // 輸出 15// 移動捕獲(適用于不可復制的對象,如 std::unique_ptr)
auto ptr = std::make_unique<int>(42);
auto lambda2 = [ptr = std::move(ptr)]() { return *ptr; };
9. Lambda 的類型和存儲
- Lambda 表達式的類型是一個唯一的、未命名的閉包類型(Closure Type)。
- 可使用
auto
或std::function
存儲 lambda。
// 使用 auto(推薦,效率更高)
auto add = [](int a, int b) { return a + b; };// 使用 std::function(需包含 <functional>)
std::function<int(int, int)> multiply = [](int a, int b) { return a * b; };
10. Lambda 在 STL 中的應用
Lambda 常用于簡化 STL 算法的使用。
#include <algorithm>
#include <vector>std::vector<int> nums = {1, 2, 3, 4, 5};// 使用 lambda 作為謂詞
auto sum = std::accumulate(nums.begin(), nums.end(), 0, [](int acc, int x) { return acc + x; });// 排序
std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; }); // 降序排序// 查找第一個大于 3 的元素
auto it = std::find_if(nums.begin(), nums.end(), [](int x) { return x > 3; });
11. 常量表達式 Lambda(C++17+)
使用 constexpr
關鍵字使 lambda 可以在編譯時求值。
constexpr auto add = [](int a, int b) { return a + b; };
static_assert(add(3, 4) == 7, "Error"); // 編譯時檢查
12. 模板 Lambda(C++20+)
使用模板參數(template <typename T>
的簡寫)使 lambda 更靈活。
auto lambda = []<typename T>(const T& a, const T& b) { return a + b; };
int sum_int = lambda(1, 2); // T 推導為 int
double sum_double = lambda(1.5, 2.5); // T 推導為 double
13. 異常規范(C++17 前)
使用 noexcept
指定 lambda 是否拋出異常。
auto safe_divide = [](double a, double b) noexcept {return b != 0 ? a / b : 0;
};
14. 性能考慮
- Lambda 通常比普通函數指針或
std::function
更高效,因為編譯器可內聯其代碼。 - 值捕獲會復制變量,可能影響性能(尤其是大對象),此時應優先使用引用捕獲。
總結
C++ 的匿名函數(Lambda)提供了強大而靈活的語法,使代碼更簡潔、更易讀。掌握捕獲列表、參數、返回類型和各種特性(如泛型、捕獲初始化)是使用 Lambda 的關鍵。合理使用 Lambda 可以顯著提升 C++ 代碼的表達力和效率。