C++ Lambda 表达式详解

Lambda 表达式是 C++11 引入的一种便捷的匿名函数对象创建方式,它允许我们在需要函数的地方直接定义函数,而不需要单独声明函数。本文档基于 lambda.cpp 文件,详细介绍 Lambda 表达式的各种用法。

基本语法

Lambda 表达式的基本语法如下:

1
2
3
[capture list](parameters) -> return_type {
// 函数体
}
  • capture list:捕获列表,指定哪些外部变量可以在 Lambda 内部使用,以及如何捕获(值捕获或引用捕获)
  • parameters:参数列表,与普通函数的参数列表类似
  • return_type:返回类型,可以省略,由编译器自动推导
  • function body:函数体,包含 Lambda 表达式的执行代码

示例代码与说明

1. 基本 Lambda 表达式

1
2
3
4
5
// 基本 Lambda 表达式
auto greet = []() {
std::cout << "Hello, Lambda!" << std::endl;
};
greet(); // 调用 Lambda 表达式

这是一个最简单的 Lambda 表达式,没有参数,也没有捕获任何变量。

2. 带参数的 Lambda 表达式

1
2
3
4
5
// 带参数的 Lambda 表达式
auto add = [](int a, int b) {
return a + b;
};
std::cout << "5 + 3 = " << add(5, 3) << std::endl;

Lambda 表达式可以像普通函数一样接受参数。

3. 显式指定返回类型的 Lambda 表达式

1
2
3
4
5
// 显式指定返回类型的 Lambda 表达式
auto multiply = [](int a, int b) -> int {
return a * b;
};
std::cout << "4 * 7 = " << multiply(4, 7) << std::endl;

当 Lambda 表达式的返回类型比较复杂或编译器无法自动推导时,可以显式指定返回类型。

4. 值捕获的 Lambda 表达式

1
2
3
4
5
6
7
8
9
// 值捕获的 Lambda 表达式
int x = 10;
auto captureByValue = [x]() {
std::cout << "Captured value: " << x << std::endl;
};
captureByValue(); // 输出: Captured value: 10

x = 20;
captureByValue(); // 仍然输出: Captured value: 10,因为是值捕获

值捕获会在 Lambda 创建时复制变量的值,之后外部变量的修改不会影响 Lambda 内部的值。

5. 引用捕获的 Lambda 表达式

1
2
3
4
5
6
7
8
9
// 引用捕获的 Lambda 表达式
int y = 10;
auto captureByReference = [&y]() {
std::cout << "Captured reference: " << y << std::endl;
};
captureByReference(); // 输出: Captured reference: 10

y = 20;
captureByReference(); // 输出: Captured reference: 20,因为是引用捕获

引用捕获不会复制变量的值,而是捕获变量的引用,因此外部变量的修改会影响 Lambda 内部的值。

6. 混合捕获的 Lambda 表达式

1
2
3
4
5
6
7
8
9
10
// 混合捕获的 Lambda 表达式
int a = 5, b = 10;
auto mixedCapture = [a, &b]() {
std::cout << "a (by value): " << a << ", b (by reference): " << b << std::endl;
};
mixedCapture(); // 输出: a (by value): 5, b (by reference): 10

a = 15;
b = 20;
mixedCapture(); // 输出: a (by value): 5, b (by reference): 20

Lambda 表达式可以同时使用值捕获和引用捕获,分别指定不同的变量。

7. 默认捕获的 Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 默认值捕获的 Lambda 表达式
int m = 1, n = 2;
auto defaultCaptureValue = [=]() {
std::cout << "m: " << m << ", n: " << n << std::endl;
};
defaultCaptureValue(); // 输出: m: 1, n: 2

// 默认引用捕获的 Lambda 表达式
auto defaultCaptureRef = [&]() {
std::cout << "m: " << m << ", n: " << n << std::endl;
};
defaultCaptureRef(); // 输出: m: 1, n: 2

m = 10;
n = 20;
defaultCaptureValue(); // 输出: m: 1, n: 2(值捕获)
defaultCaptureRef(); // 输出: m: 10, n: 20(引用捕获)
  • [=]:默认值捕获,捕获所有在 Lambda 内部使用的外部变量
  • [&]:默认引用捕获,捕获所有在 Lambda 内部使用的外部变量的引用

8. mutable Lambda 表达式

1
2
3
4
5
6
7
8
9
10
// mutable Lambda 表达式
int counter = 0;
auto increment = [counter]() mutable {
counter++;
std::cout << "Counter: " << counter << std::endl;
};
increment(); // 输出: Counter: 1
increment(); // 输出: Counter: 2
increment(); // 输出: Counter: 3
std::cout << "Original counter: " << counter << std::endl; // 输出: Original counter: 0

默认情况下,值捕获的变量在 Lambda 内部是不可修改的。使用 mutable 关键字可以允许修改值捕获的变量,但这不会影响外部的原始变量。

9. Lambda 表达式与 STL 算法

9.1 for_each 算法

1
2
3
4
5
// 使用 Lambda 表达式的 for_each 算法
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::for_each(numbers.begin(), numbers.end(), [](int& n) {
n *= 2;
});

9.2 count_if 算法

1
2
3
4
5
// 使用 Lambda 表达式的 count_if 算法
auto evenNumbers = std::count_if(numbers.begin(), numbers.end(), [](int n) {
return n % 2 == 0;
});
std::cout << "Count of even numbers: " << evenNumbers << std::endl;

9.3 find_if 算法

1
2
3
4
5
6
7
// 使用 Lambda 表达式的 find_if 算法
auto greaterThanTen = std::find_if(numbers.begin(), numbers.end(), [](int n) {
return n > 10;
});
if (greaterThanTen != numbers.end()) {
std::cout << "First number > 10: " << *greaterThanTen << std::endl;
}

9.4 sort 算法

1
2
3
4
5
6
7
8
9
10
11
12
// 使用 Lambda 表达式的 sort 算法
std::vector<int> unsorted = {5, 2, 8, 1, 9, 3, 7, 4, 6};

// 升序排序
std::sort(unsorted.begin(), unsorted.end(), [](int a, int b) {
return a < b;
});

// 降序排序
std::sort(unsorted.begin(), unsorted.end(), [](int a, int b) {
return a > b;
});

9.5 transform 算法

1
2
3
4
5
6
// 使用 Lambda 表达式的 transform 算法
std::vector<int> original = {1, 2, 3, 4, 5};
std::vector<int> squared(original.size());

std::transform(original.begin(), original.end(), squared.begin(),
[](int n) { return n * n; });

9.6 accumulate 算法

1
2
3
4
5
6
7
8
9
10
11
// 使用 Lambda 表达式的 accumulate 算法
std::vector<int> values = {1, 2, 3, 4, 5};
int sum = std::accumulate(values.begin(), values.end(), 0, [](int acc, int n) {
return acc + n;
});
std::cout << "Sum: " << sum << std::endl;

int product = std::accumulate(values.begin(), values.end(), 1, [](int acc, int n) {
return acc * n;
});
std::cout << "Product: " << product << std::endl;

9.7 remove_if 算法

1
2
3
4
5
6
// 使用 Lambda 表达式的 remove_if 算法
std::vector<int> toRemove = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto newEnd = std::remove_if(toRemove.begin(), toRemove.end(), [](int n) {
return n % 2 == 0;
});
toRemove.erase(newEnd, toRemove.end()); // 真正删除元素

9.8 copy_if 算法

1
2
3
4
5
6
7
// 使用 Lambda 表达式的 copy_if 算法
std::vector<int> source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> dest;

std::copy_if(source.begin(), source.end(), std::back_inserter(dest), [](int n) {
return n % 2 != 0;
});

10. Lambda 表达式与 std::function

1
2
3
4
5
6
7
8
9
10
11
// Lambda 表达式与 std::function
std::function<int(int, int)> operation = [](int a, int b) {
return a + b;
};
std::cout << "Operation(3, 4) = " << operation(3, 4) << std::endl;

// 可以重新赋值不同的 Lambda 表达式
operation = [](int a, int b) {
return a * b;
};
std::cout << "Operation(3, 4) = " << operation(3, 4) << std::endl;

使用 std::function 可以存储 Lambda 表达式,方便在不同地方使用。

11. Lambda 表达式作为函数参数

1
2
3
4
5
6
7
8
9
// Lambda 表达式作为函数参数
auto applyOperation = [](std::vector<int>& vec, std::function<int(int)> op) {
for (int& n : vec) {
n = op(n);
}
};

std::vector<int> nums = {1, 2, 3, 4, 5};
applyOperation(nums, [](int n) { return n * n; }); // 传递 Lambda 作为参数

Lambda 表达式可以作为参数传递给其他函数,特别是接受函数对象的 STL 算法。

12. Lambda 表达式与字符串

1
2
3
4
5
6
7
8
9
10
11
12
// Lambda 表达式与字符串
std::vector<std::string> words = {"apple", "banana", "cherry", "date", "elderberry"};

// 按字符串长度排序
std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
return a.length() < b.length();
});

// 统计长度大于 5 的单词数量
auto longWords = std::count_if(words.begin(), words.end(), [](const std::string& w) {
return w.length() > 5;
});

Lambda 表达式同样适用于处理字符串等复杂类型。

额外示例

1. 嵌套 Lambda 表达式

1
2
3
4
5
6
7
8
9
// 嵌套 Lambda 表达式
auto outerLambda = [](int x) {
return [x](int y) {
return x + y;
};
};

auto add5 = outerLambda(5);
std::cout << "5 + 3 = " << add5(3) << std::endl; // 输出: 8

Lambda 表达式可以嵌套,内部 Lambda 可以访问外部 Lambda 捕获的变量。

2. 带初始化捕获的 Lambda 表达式(C++14)

1
2
3
4
5
// 带初始化捕获的 Lambda 表达式(C++14)
auto lambdaWithInit = [x = 10]() {
std::cout << "Initialized value: " << x << std::endl;
};
lambdaWithInit(); // 输出: Initialized value: 10

C++14 引入了初始化捕获,允许在捕获列表中初始化变量。

3. 泛型 Lambda 表达式(C++14)

1
2
3
4
5
6
7
8
// 泛型 Lambda 表达式(C++14)
auto genericLambda = [](auto a, auto b) {
return a + b;
};

std::cout << "5 + 3 = " << genericLambda(5, 3) << std::endl; // 整数相加
std::cout << "5.5 + 3.3 = " << genericLambda(5.5, 3.3) << std::endl; // 浮点数相加
std::cout << "Hello " + "World" << std::endl; // 字符串相加

C++14 引入了泛型 Lambda 表达式,使用 auto 关键字作为参数类型。

4. Lambda 表达式与智能指针

1
2
3
4
5
6
7
8
9
10
11
// Lambda 表达式与智能指针
std::vector<std::shared_ptr<int>> ptrs;
ptrs.push_back(std::make_shared<int>(10));
ptrs.push_back(std::make_shared<int>(20));
ptrs.push_back(std::make_shared<int>(30));

// 使用 Lambda 表达式遍历智能指针
std::for_each(ptrs.begin(), ptrs.end(), [](const std::shared_ptr<int>& p) {
std::cout << *p << " ";
});
std::cout << std::endl;

Lambda 表达式可以方便地处理智能指针等复杂类型。

编译与运行

使用以下命令编译 lambda.cpp 文件:

1
g++ -fexec-charset=GBK lambda.cpp -o lambda.exe

其中 -fexec-charset=GBK 参数指定编译后的可执行文件使用 GBK 字符集,确保中文输出正常显示。

运行编译后的程序:

1
lambda.exe

总结

Lambda 表达式是 C++11 引入的一种强大特性,它允许我们在需要函数的地方直接定义函数,而不需要单独声明函数。Lambda 表达式的主要优势包括:

  1. 简洁性:可以在需要的地方直接定义函数,不需要单独声明
  2. 灵活性:支持值捕获和引用捕获,可以根据需要选择不同的捕获方式
  3. 强大的表达能力:可以与 STL 算法无缝配合,使代码更加简洁易读
  4. 闭包特性:可以捕获外部变量,形成闭包,使函数更加灵活

通过本文档的介绍,相信您已经对 Lambda 表达式的各种用法有了详细的了解。在实际编程中,合理使用 Lambda 表达式可以使代码更加简洁、易读和高效。

lambda.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <string>
#include <numeric>

int main() {
std::cout << "=== Basic Lambda ===" << std::endl;
auto greet = []() {
std::cout << "Hello, Lambda!" << std::endl;
};
greet();

std::cout << "\n=== Lambda with Parameters ===" << std::endl;
auto add = [](int a, int b) {
return a + b;
};
std::cout << "5 + 3 = " << add(5, 3) << std::endl;

std::cout << "\n=== Lambda with Return Type ===" << std::endl;
auto multiply = [](int a, int b) -> int {
return a * b;
};
std::cout << "4 * 7 = " << multiply(4, 7) << std::endl;

std::cout << "\n=== Lambda Capturing by Value ===" << std::endl;
int x = 10;
auto captureByValue = [x]() {
std::cout << "Captured value: " << x << std::endl;
};
captureByValue();

x = 20;
captureByValue();

std::cout << "\n=== Lambda Capturing by Reference ===" << std::endl;
int y = 10;
auto captureByReference = [&y]() {
std::cout << "Captured reference: " << y << std::endl;
};
captureByReference();

y = 20;
captureByReference();

std::cout << "\n=== Lambda with Mixed Capture ===" << std::endl;
int a = 5, b = 10;
auto mixedCapture = [a, &b]() {
std::cout << "a (by value): " << a << ", b (by reference): " << b << std::endl;
};
mixedCapture();

a = 15;
b = 20;
mixedCapture();

std::cout << "\n=== Lambda with Default Capture ===" << std::endl;
int m = 1, n = 2;
auto defaultCaptureValue = [=]() {
std::cout << "m: " << m << ", n: " << n << std::endl;
};
defaultCaptureValue();

auto defaultCaptureRef = [&]() {
std::cout << "m: " << m << ", n: " << n << std::endl;
};
defaultCaptureRef();

m = 10;
n = 20;
defaultCaptureValue();
defaultCaptureRef();

std::cout << "\n=== Lambda with Mutable ===" << std::endl;
int counter = 0;
auto increment = [counter]() mutable {
counter++;
std::cout << "Counter: " << counter << std::endl;
};
increment();
increment();
increment();
std::cout << "Original counter: " << counter << std::endl;

std::cout << "\n=== Lambda with STL Algorithms ===" << std::endl;
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

std::cout << "Original: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::for_each(numbers.begin(), numbers.end(), [](int& n) {
n *= 2;
});

std::cout << "After for_each (multiply by 2): ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

auto evenNumbers = std::count_if(numbers.begin(), numbers.end(), [](int n) {
return n % 2 == 0;
});
std::cout << "Count of even numbers: " << evenNumbers << std::endl;

auto greaterThanTen = std::find_if(numbers.begin(), numbers.end(), [](int n) {
return n > 10;
});
if (greaterThanTen != numbers.end()) {
std::cout << "First number > 10: " << *greaterThanTen << std::endl;
}

std::cout << "\n=== Lambda with Sort ===" << std::endl;
std::vector<int> unsorted = {5, 2, 8, 1, 9, 3, 7, 4, 6};

std::sort(unsorted.begin(), unsorted.end(), [](int a, int b) {
return a < b;
});
std::cout << "Ascending: ";
for (int num : unsorted) {
std::cout << num << " ";
}
std::cout << std::endl;

std::sort(unsorted.begin(), unsorted.end(), [](int a, int b) {
return a > b;
});
std::cout << "Descending: ";
for (int num : unsorted) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "\n=== Lambda with Transform ===" << std::endl;
std::vector<int> original = {1, 2, 3, 4, 5};
std::vector<int> squared(original.size());

std::transform(original.begin(), original.end(), squared.begin(),
[](int n) { return n * n; });

std::cout << "Squared: ";
for (int num : squared) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "\n=== Lambda with std::function ===" << std::endl;
std::function<int(int, int)> operation = [](int a, int b) {
return a + b;
};
std::cout << "Operation(3, 4) = " << operation(3, 4) << std::endl;

operation = [](int a, int b) {
return a * b;
};
std::cout << "Operation(3, 4) = " << operation(3, 4) << std::endl;

std::cout << "\n=== Lambda as Function Parameter ===" << std::endl;
auto applyOperation = [](std::vector<int>& vec, std::function<int(int)> op) {
for (int& n : vec) {
n = op(n);
}
};

std::vector<int> nums = {1, 2, 3, 4, 5};
applyOperation(nums, [](int n) { return n * n; });
std::cout << "Squared: ";
for (int num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "\n=== Lambda with String ===" << std::endl;
std::vector<std::string> words = {"apple", "banana", "cherry", "date", "elderberry"};

std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
return a.length() < b.length();
});

std::cout << "Sorted by length: ";
for (const auto& word : words) {
std::cout << word << " ";
}
std::cout << std::endl;

auto longWords = std::count_if(words.begin(), words.end(), [](const std::string& w) {
return w.length() > 5;
});
std::cout << "Count of words with length > 5: " << longWords << std::endl;

std::cout << "\n=== Lambda with Accumulate ===" << std::endl;
std::vector<int> values = {1, 2, 3, 4, 5};
int sum = std::accumulate(values.begin(), values.end(), 0, [](int acc, int n) {
return acc + n;
});
std::cout << "Sum: " << sum << std::endl;

int product = std::accumulate(values.begin(), values.end(), 1, [](int acc, int n) {
return acc * n;
});
std::cout << "Product: " << product << std::endl;

std::cout << "\n=== Lambda with Remove_if ===" << std::endl;
std::vector<int> toRemove = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto newEnd = std::remove_if(toRemove.begin(), toRemove.end(), [](int n) {
return n % 2 == 0;
});
toRemove.erase(newEnd, toRemove.end());

std::cout << "After removing even numbers: ";
for (int num : toRemove) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "\n=== Lambda with Copy_if ===" << std::endl;
std::vector<int> source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> dest;

std::copy_if(source.begin(), source.end(), std::back_inserter(dest), [](int n) {
return n % 2 != 0;
});

std::cout << "Copied odd numbers: ";
for (int num : dest) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}