29-自适应函数符和函数适配器
16.5.3 自适应函数符和函数适配器
表16.12列出的预定义函数符都是自适应的。实际上STL有5个相关的概念:自适应生成器(adaptable generator)、自适应一元函数(adaptable unary function)、自适应二元函数(adaptable binary function)、自适应谓词(adaptable predicate)和自适应二元谓词(adaptable binary predicate)。
使函数符成为自适应的原因是,它携带了标识参数类型和返回类型的typedef成员。这些成员分别是result_type、first_argument_type和second_argument_type,它们的作用是不言自明的。例如,plus
函数符自适应性的意义在于:函数适配器对象可以使用函数对象,并认为存在这些typedef成员。例如,接受一个自适应函数符参数的函数可以使用result_type成员来声明一个与函数的返回类型匹配的变量。
STL提供了使用这些工具的函数适配器类。例如,假设要将矢量gr8的每个元素都增加2.5倍,则需要使用接受一个一元函数参数的transform()版本,就像前面的例子那样:
transform(gr8.begin(), gr8.end(), out, sqrt);
multiplies()函数符可以执行乘法运行,但它是二元函数。因此需要一个函数适配器,将接受两个参数的函数符转换为接受1个参数的函数符。前面的TooBig2示例提供了一种方法,但STL使用binder1st和binder2nd类自动完成这一过程,它们将自适应二元函数转换为自适应一元函数。
来看binder1st。假设有一个自适应二元函数对象f2(),则可以创建一个binder1st对象,该对象与一个将被用作f2()的第一个参数的特定值(val)相关联:
binder1st(f2, val) f1;
这样,使用单个参数调用f1(x)时,返回的值与将val作为第一参数、将f1()的参数作为第二参数调用f2()返回的值相同。即f1(x)等价于f2(val, x),只是前者是一元函数,而不是二元函数。f2()函数被适配。同样,仅当f2()是一个自适应函数时,这才能实现。
看上去有点麻烦。然而,STL提供了函数bind1st(),以简化binder1st类的使用。可以问其提供用于构建binder1st对象的函数名称和值,它将返回一个这种类型的对象。例如,要将二元函数multiplies()转换为将参数乘以2.5的一元函数,则可以这样做:
bind1st(multiplies<double>(), 2.5)
因此,将gr8中的每个元素与2.5相乘,并显示结果的代码如下:
transform(gr8.begin(), gr8.end(), out,
bind1st(multiplies<double>(), 2.5));
binder2nd类与此类似,只是将常数赋给第二个参数,而不是第一个参数。它有一个名为bind2nd的助手函数,该函数的工作方式类似于bind1st。
程序清单16.16将一些最近的示例合并成了一个小程序。
程序清单16.16 funadap.cpp
// funadap.cpp -- using function adapters
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
void Show(double);
const int LIM = 6;
int main()
{
using namespace std;
double arr1[LIM] = {28, 29, 30, 35, 38, 59};
double arr2[LIM] = {63, 65, 69, 75, 80, 99};
vector<double> gr8(arr1, arr1 + LIM);
vector<double> m8(arr2, arr2 + LIM);
cout.setf(ios_base::fixed);
cout.precision(1);
cout << "gr8:\t";
for_each(gr8.begin(), gr8.end(), Show);
cout << endl;
cout << "m8: \t";
for_each(m8.begin(), m8.end(), Show);
cout << endl;
vector<double> sum(LIM);
transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(),
plus<double>());
cout << "sum:\t";
for_each(sum.begin(), sum.end(), Show);
cout << endl;
vector<double> prod(LIM);
transform(gr8.begin(), gr8.end(), prod.begin(),
bind1st(multiplies<double>(), 2.5));
cout << "prod:\t";
for_each(prod.begin(), prod.end(), Show);
cout << endl;
return 0;
}
void Show(double v)
{
std::cout.width(6);
std::cout << v << ' ';
}
程序清单16.16中程序的输出如下:
gr8: 28.0 29.0 30.0 35.0 38.0 59.0
m8: 63.0 65.0 69.0 75.0 80.0 99.0
sum: 91.0 94.0 99.0 110.0 118.0 158.0
prod: 70.0 72.5 75.0 87.5 95.0 147.5
C++11提供了函数指针和函数符的替代品——lambda表达式,这将在第18章讨论。