1.2 函数对象
我将介绍STL使用的一些基本的技术,它会让你了解:在普通C++机制上建立的STL是如何提供空前的灵活性和性能的。迄今为止,我们所描述的STL框架组件都有些严格。每种算法都严格地采用标准指定的方法来准确地实现某种功能。例如,我们需要查找一个与自己指定的值相等的元素。实际上,查找一个带有某些(自己指定的)属性的元素,例如小于某个给定的值、匹配某个并非简单相等的值(例如,匹配大小写无关的字符串或者在允许很小差别的情况下,匹配双精度数值),是一项很普通的事务。
下面的例子不是查找值7,我们将查找某些符合条件的值(也就是小于7的值):
vector
if (p != vi.end()) {
// 我们找到了值小于7 的元素
// …
}
else {
// vi 没有值小于 7 的元素
// …
}
template
T value;
Less_than(const T& v) :value(v) { }
bool operator()(const T& v) const { return v
Less_than
bool b1 = f(3); // b1 为真(3<3.14 是真的)
bool b2 = f(4); // b2 为假(4<3.14 是假的)
我们可以编写一个find()版本,它使用了函数对象,而不是简单的!=来检测是否可以找到某个元素。它的名字是find_if():
template
In find_if(In first, In last, Pred pred)
{
while (first!=last && !pred(*first)) ++first;
return first;
}
bool less_than_7(int a)
{
return 7}
vector
template
T value;
int count;
Accumulator() :value(), count(0) { }
Accumulator(const T& v) :value(v), count(0) { }
void operator()(const T& v) { ++count; value+=v; }
};
int main()
{
vector
double d;
while (cin>>d) v.push_back(d);
Accumulator
ad = for_each(v.begin(),v.end(), ad);
cout << "sum==" << ad.value
<< ", mean==" << ad.value/ad.count << '\n';
}
有趣的是,简单的函数对象比功能相同的函数的性能更好。其原因在于它们趋向于按值(by value)传递,因此它们更易于内联(inline)。当我们传递那些执行很简单操作(例如用于排序的比较条件)的对象或函数的时候,这一点是非常重要的。特别地,当对简单类型(例如整型或双精度型)的数组进行排序的时候,函数对象的内联就是STL(C++标准类库)的sort()在多方面超越了传统的qsort()原因。
函数对象是用于更高级构造的一种C++机制。它并非高级理念的最好表达方式,但是它在用于普通目的的语言环境中,显示出令人惊讶的表现能力和固有的高性能。作为表现能力的一个例子,Jaakko J?rvi演示了如何提供和使用一个Lambda类,使它的真正意义合法化:
Lambda x;
List
class Lambda {};
template
{
return Less_than
}
1.3 STL的影响
STL对C++的思想的影响是极大的。在STL之前,我一般列举C++支持的三种基本的编程样式("范例"):
-面向过程编程
-数据抽象
-面向对象编程
我认为模板是对数据抽象的支持。在试用了STL一段时间之后,我提出第四种样式:
-泛型编程
以使用模板为基础的技术,以及从功能性编程中获取大量灵感的技术与传统的数据抽象有本质的不同。人们只是认为类型、对象和资源不同。新的C++类库是使用模板技术编写的,才获得了静态类型安全和高效率。模板技术对于嵌入式系统编程和高性能数学运算也是很关键的,在这些环境中,资源的管理和正确性是关键。在这些领域中STL并非总是理想的。例如,它没有直接地支持线性代数,而且在紧凑的实时系统中(在这种环境下一般会禁止自由地使用存储)也很难使用它。但是,STL证明了在模板的帮助下可以实现什么样的功能,并提出了一些有效的技术示例。例如,利用迭代子(和分配器)把逻辑内存访问与实际内存访问分离开来,对于很多高性能数字运算就是很关键的;使用小型的、简单的内联、对象对于嵌入式系统编程中最佳地使用硬件也是很关键的。这类技术有一些记载在标准委员会关于性能的技术报告中了。这是对当前过度地使用过分依赖于类层次和虚拟函数的"面向对象"技术的这种趋势的一种大范围的反击--也是一种有建设意义的替代方案。
很明显,STL并不完美。相对来说没有完美的东西。但是,它开辟了新天地,而且它拥有的影响力甚至于超过了巨大的C++群体。使用C++时,当人们试图推动STL所倡导的技术来超越STL技术的时候,它们讨论"模板元数据编程"。我们中有些人也会考虑STL迭代子的限制(使用generator和range是不是更好?),以及C++如何更好地支持这些使用(概念、初始化器)。
