函数对象和Lambda表达式是C++中的两个强大功能,用于实现可调用对象和简化代码。下面是对它们的详细讲解及其用法。
函数对象(Function Object)
函数对象(或称仿函数,Functor)是一个定义了operator()的类的实例。它们通常用于需要可调用对象的地方,如标准库算法。
定义和使用函数对象
一个简单的函数对象例子:
#include <iostream>
#include <vector>
#include <algorithm>
// 定义函数对象
class Add {
public:
Add(int value) : value_(value) {}
int operator()(int x) const {
return x + value_;
}
private:
int value_;
};
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用函数对象
Add add5(5);
std::transform(vec.begin(), vec.end(), vec.begin(), add5);
for (int n : vec) {
std::cout << n << " "; // 输出 6 7 8 9 10
}
return 0;
}
在上述代码中,Add类定义了一个函数对象,它接受一个整数并返回加上某个值的结果。在main函数中,std::transform使用这个函数对象来将向量中的每个元素都加上5。
Lambda表达式
Lambda表达式是C++11引入的,用于定义匿名函数。它们使得在需要临时函数对象的地方编写代码更加简洁。
基本语法
Lambda表达式的基本语法如下:
[capture](parameters) -> return_type {
body
};
capture:捕获外部变量的方式。
parameters:参数列表。
return_type:返回类型(可以省略,由编译器推断)。
body:函数体。
示例
一个简单的Lambda表达式例子:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用Lambda表达式
std::transform(vec.begin(), vec.end(), vec.begin(), [](int x) {
return x + 5;
});
for (int n : vec) {
std::cout << n << " "; // 输出 6 7 8 9 10
}
return 0;
}
在上述代码中,std::transform使用一个Lambda表达式来将向量中的每个元素都加上5。Lambda表达式[](int x) { return x + 5; }定义了一个匿名函数,该函数接受一个整数并返回加上5的结果。
捕获外部变量
Lambda表达式可以捕获外部变量,使其在Lambda表达式的函数体中可用。捕获方式有两种:按值捕获和按引用捕获。
按值捕获:外部变量的值被复制到Lambda表达式中。
按引用捕获:Lambda表达式中使用外部变量的引用。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
int addValue = 5;
// 按值捕获
std::transform(vec.begin(), vec.end(), vec.begin(), [addValue](int x) {
return x + addValue;
});
for (int n : vec) {
std::cout << n << " "; // 输出 6 7 8 9 10
}
std::cout << std::endl;
// 按引用捕获
addValue = 10;
std::transform(vec.begin(), vec.end(), vec.begin(), [&addValue](int x) {
return x + addValue;
});
for (int n : vec) {
std::cout << n << " "; // 输出 16 17 18 19 20
}
return 0;
}
在上述代码中,第一次std::transform使用按值捕获,将addValue的值复制到Lambda表达式中,因此addValue的变化不会影响Lambda表达式的结果。第二次std::transform使用按引用捕获,因此addValue的变化会影响Lambda表达式的结果。
总结
函数对象和Lambda表达式都是实现可调用对象的方式。函数对象通过定义类和operator()来实现,适合较复杂的场景;Lambda表达式则通过简洁的语法定义匿名函数,适合临时性和简单的可调用对象。通过这两种方式,可以极大地提高代码的灵活性和可读性。