在 C++ 中,使用仿函数(functors),也称为函数对象(function objects),来表示带有状态的计算是一种常见的做法。例如,考虑一个使用算法将整数打印到标准输出的程序:
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
void display(int n) { cout << n << ' '; }
int main() {
int vals[]{ 2,3,5,7,11 };
for_each(begin(vals), end(vals), display);
}
这个小程序运行良好,但如果希望将输出输出到非标准输出的地方(比如一个文件或字符串流),我们就会陷入一个尴尬的境地:for_each() 算法所接受的“函数”是一个一元函数(接受单个参数的函数),在这里是待输出印的值。因此,语法上没有空间添加像“输出流”这样的额外参数。可以通过全局变量来“解决”这个问题,或者为每个输出流编写不同的函数,但这些方法都达不到合理的设计标准。
如果把 display 函数替换为一个类(将其命名为 Display 以在视觉上区分类和函数),最终将得到如下代码:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <fstream>
using namespace std;
class Display {
ostream &os;
public:
Display(ostream &os) : os{ os } {
}
void operator()(int n) const { os << n << ' '; }
};
int main() {
int vals[]{ 2,3,5,7,11 };
// 在标准输出上显示
for_each(begin(vals), end(vals), Display{ cout });
ofstream out{"out.txt" };
// 写入文件 out.txt
for_each(begin(vals), end(vals), Display{ out });
}
这样得到的代码更清晰、更具可读性,并且提供了更大的灵活性。从概念上讲,lambda 表达式本质上就是仿函数(甚至可以将 lambda 用作基类!),因此上面的例子也可以等价地重写为:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <fstream>
using namespace std;
int main() {
int vals[]{ 2,3,5,7,11 };
// 在标准输出上显示
for_each(begin(vals), end(vals), [](int n) {
cout << n << ' ';
});
ofstream out{"out.txt" };
// 写入文件 out.txt
for_each(begin(vals), end(vals), [&out](int n) {
out << n << ' ';
});
}
因此,lambda 表达式本质上就是只包含构造函数和 operator() 成员函数的仿函数。而这种组合,迄今为止是这类对象最常见的情形。当然,如果需要更多功能,仍然可以使用完整、显式的仿函数。