C++ 函数式编程Lambda表达式
C++ 是一门支持多种编程范式的语言,包括 函数式编程。在函数式编程中,函数被视为一等公民,可以像数据一样传递、返回,甚至被赋值给变量。C++ 引入的 Lambda 表达式 是支持函数式编程的一个重要特性。接下来,我们将详细介绍 函数式编程 在 C++ 中的应用,以及 Lambda 表达式 的语法、变量捕获规则和泛型用法。
一、C++中的函数式编程
函数式编程(Functional Programming) 是一种编程范式,其中函数是 一等公民(first-class citizens)。这意味着函数可以像其他数据类型一样被传递、赋值、返回等。
C++ 作为一门多范式语言,既支持面向对象编程(OOP),也支持函数式编程。C++11 引入了 Lambda 表达式,使得函数式编程在 C++ 中变得更加自然。
1. 函数作为一等公民
在 C++ 中,函数可以像数据一样被操作。您可以通过以下方式将函数作为一等公民使用:
- 将函数指针作为参数传递给其他函数
- 将函数作为返回值返回
- 将函数指针赋值给变量
例如:
#include <iostream>
using namespace std;
// 普通函数
int add(int x, int y) {
return x + y;
}
// 将函数作为参数传递
void operate(int x, int y, int (*func)(int, int)) {
cout << "Result: " << func(x, y) << endl;
}
int main() {
// 将 add 函数作为参数传递
operate(5, 3, add); // 输出 Result: 8
return 0;
}
二、Lambda 表达式
Lambda 表达式 是 C++11 引入的一个强大的特性,它让我们能够方便地定义匿名函数并在需要时使用它们。Lambda 表达式特别适合在算法、回调和并发等场景中使用。
1. Lambda 表达式的语法形式
Lambda 表达式的基本语法如下:
[捕获列表](参数列表) -> 返回类型 { 函数体 }
- 捕获列表(
[]
):指定如何捕获外部变量。可以按值捕获、按引用捕获等。 - 参数列表(
(int x)
):和普通函数一样,Lambda 表达式可以接受参数。 - 返回类型(
-> 返回类型
):指定 Lambda 的返回类型。如果返回类型可以自动推导,可以省略。 - 函数体(
{}
):Lambda 表达式的实际实现。
示例:
#include <iostream>
using namespace std;
int main() {
// Lambda 表达式,计算两个数的和
auto add = [](int a, int b) -> int {
return a + b;
};
cout << add(3, 4) << endl; // 输出 7
return 0;
}
在这个示例中,我们定义了一个 Lambda 表达式 add
,它接受两个整数参数并返回它们的和。
2. 变量捕获规则
Lambda 表达式可以访问外部作用域中的变量。通过 捕获列表([]
) 来指定如何捕获外部变量。C++ 支持按 值捕获 和 按引用捕获 两种方式。
- 按值捕获(
[=]
):捕获外部变量的副本。 - 按引用捕获(
[&]
):捕获外部变量的引用。 - 显式捕获:指定单个或多个变量的捕获方式
示例:
#include <iostream>
using namespace std;
int main() {
int n = 10;
int x = 5, y = 10;
// 按值捕获外部变量 n
auto byValue = [=](int x) {
cout << "By Value: " << x * n << endl; // 使用捕获的副本 n
};
// 按引用捕获外部变量 n
auto byReference = [&](int x) {
cout << "By Reference: " << x * n << endl; // 使用 n 的引用
n = 20; // 修改 n
};
auto printXY = [=, &y]() {
cout << "x = " << x << ", y = " << y << endl; // x 按值捕获,y 按引用捕获
y = 20; // 修改 y
};
byValue(3); // 输出 By Value: 30
cout << "n after byValue: " << n << endl; // 输出 n after byValue: 10 (n 不变)
byReference(3); // 输出 By Reference: 30
cout << "n after byReference: " << n << endl; // 输出 n after byReference: 20 (n 被修改)
return 0;
}
byValue
按值捕获外部变量n
,Lambda 内部使用的是n
的副本,修改外部n
不会影响 Lambda 内部的副本。byReference
按引用捕获外部变量n
,Lambda 内部直接操作外部n
,修改n
会影响外部n
。
3. 泛型 Lambda
C++14 引入了 泛型 Lambda(Generic Lambda),允许我们在 Lambda 表达式中使用 auto
关键字来声明参数类型,从而使得 Lambda 可以适用于多种类型。
示例:
#include <iostream>
using namespace std;
int main() {
// 泛型 Lambda
auto print = [](auto x) {
cout << x << endl;
};
print(42); // 输出 42
print(3.14); // 输出 3.14
print("Hello"); // 输出 Hello
return 0;
}
在这个示例中,Lambda 使用 auto
来表示它可以接受任何类型的参数,且能够根据传入参数的类型自动推导。
泛型 Lambda 与模板函数的对比:
- 模板函数:在编译时确定函数的类型。
- 泛型 Lambda:提供了更简洁的语法,能够在运行时根据输入的类型自动推导。
三、Lambda 表达式的完整示例
让我们结合之前的内容,创建一个更为完整的 Lambda 表达式示例,涵盖捕获规则、参数、返回类型和泛型 Lambda:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// 定义一个包含整数的向量
vector<int> nums = {1, 2, 3, 4, 5};
// 按值捕获并传递参数,计算平方
int multiplier = 2;
auto squareAndMultiply = [=](int x) -> int {
return x * x * multiplier;
};
// 使用标准库算法 for_each 传递 Lambda 表达式
for_each(nums.begin(), nums.end(), [&](int n) {
cout << squareAndMultiply(n) << " "; // 输出平方并乘以 multiplier
});
cout << endl;
// 泛型 Lambda 用于不同类型的数据
auto print = [](auto x) {
cout << x << endl;
};
print(42); // 输出 42
print(3.14); // 输出 3.14
print("Hello"); // 输出 Hello
return 0;
}
总结
-
Lambda 表达式:C++11 引入的匿名函数,支持捕获外部变量并简洁地表达函数逻辑。
- 基本语法:捕获列表 -> 返回类型 { 函数体 }
-
变量捕获规则:
[=]
按值捕获外部变量的副本。[&]
按引用捕获外部变量的引用。
-
泛型 Lambda:C++14 引入,允许 Lambda 使用
auto
来接受任何类型的参数,实现类似模板函数的效果。