Effective STL 条款36

类别:编程语言 点击:0 评论:0 推荐:
条款36:了解copy_if的正确实现

STL有很多有趣的地方,其中一个是虽然有11个名字带“copy”的算法:

copy copy_backward replace_copy reverse_copy replace_copy_if unique_copy remove_copy rotate_copy remove_copy_if partial_sort_copy unintialized_copy  

但没有一个是copy_if。这意味着你可以replace_copy_if,你可以remove_copy_if,你可以copy_backward或者reverse_copy,但如果你只是简单地想要拷贝一个区间中满足某个判断式(predicate)的元素,你只能自己做。

比如,假设你有一个函数来决定一个Widget是否有缺陷的:

bool isDefective(const Widget& w);

而且你希望把一个vector中所有有缺陷的Widget写到cerr。如果存在copy_if,你可以简单地这么做:

vector<Widget> widgets; ... copy_if(widgets.begin(), widgets.end(), // 这无法编译: ostream_iterator<Widget>(cerr, "\n"), // STL中并没有copyif isDefective);

具有讽刺意味的事,copy_if是最初的惠普STL的一部分,惠普STL形成了STL的基础,而STL现在是标准C++库的一部分。有些怪癖会使历史变得有趣,在从HP STL中为标准筛选出一些大小易于管理的东西时,copy_if成了其中一个在剪接室中被遗弃的东西。

在《The C++ Programming Language》[7],Stroustrup强调写copy_if是微不足道的,他是对的,但那不意味着实现正确的琐事很简单。比如,这里有一个合理的看待copy_if,很多人(包括我)曾经知道的实现:

template<typename InputIterator, // 一个不很正确的 typename OutputIterator, // copy_if实现 typename Predicate> OutputIterator copy_if(InputIterator begin, InputIterator end, OutputIterator destBegin, Predicate p) { return remove_copy_if(begin, end, destBegin, not1(p)); }

这个方法是基于这个观点——虽然STL并没有让你说“拷贝每个判断式为true的东西”,但它的确让你说了“拷贝除了判断式不为true以外的每个东西”。要实现copy_if,似乎我们需要做的就只是加一个not1在我们希望传给copy_if的判断式前面,然后把这个结果判断式传给remove_copy_if,结果就是上面的代码。

如果上面的理由有效,我们就可以用这种方法写出有缺陷的Widget:

copy_if(widgets.begin(), widgets.end(), // well-intentioned code ostream_iterator<Widget>(cerr, "\n"), // that will not compile isDefective);

你的STL平台将会敌视这段代码,因为它试图对isDefective应用not1(这个应用出现在copy_if内部)。正如条款41试图将清楚的,not1不能直接应用于一个函数指针,函数指针必须先传给ptr_fun。要调用这个copy_if实现,你必须传递的不仅是一个函数对象,而且是一个可适配的函数对象。这够简单了,但是想要成为STL算法用户的人应该不必这样。标准STL算法从来不要求它们的仿函数是可适配的,copy_if也不应该要求。上面的实现是好的,但不够好。

这是copy_if正确的微不足道的实现:

template<typename InputIterator, // 一个copy_if的 typename OutputIterator, // 正确实现 typename Predicate> OutputIterator copy_if(InputIterator begin, InputIterator end, OutputIterator destBegin, Predicate p) { while (begin != end) { if (p(*begin))*destBegin++ = *begin; ++begin; } return destBegin; }

告诉你copy_if多有用,加上新STL程序员趋向于希望无论如何它应该存在的事实,所以好的做法是把copy_if——正确的那个!——放在你局部的STL相关工具库中,而且只要合适就使用。

本文地址:http://com.8s8s.com/it/it28969.htm