CSDN博客

img AMin2001

Effective STL 条款36

发表于2004/3/5 13:47:00  1555人阅读

条款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相关工具库中,而且只要合适就使用。

0 0

相关博文

我的热门文章

img
取 消
img