22

这可能是 STL 中命名最差的函数吗?(反问)

std::remove_copy_if() 实际上似乎没有做任何删除。据我所知,它的行为更像 copy_if_not。

否定有点令人困惑,但可以用 std::not1() 解决,但是我可能会误解一些东西,因为我无法理解这个函数与删除有什么关系——我错过了什么吗?

如果没有,是否有 STL 算法有条件地从容器中删除(移动?)元素并将它们放入另一个容器中?

编辑以添加示例,以便读者减少困惑。

以下程序似乎使输入范围(V1)保持不变:

#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>

using std::cout;
using std::endl;

int main (void)
{
    std::vector<int> V1, V2;
    V1.push_back(-2);
    V1.push_back(0);
    V1.push_back(-1);
    V1.push_back(0);
    V1.push_back(1);
    V1.push_back(2);

    std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
    cout << endl;

    std::remove_copy_if(
        V1.begin(),
        V1.end(),
        std::back_inserter(V2),
        std::bind2nd(std::less<int>(), 0));

    std::copy(V2.begin(), V2.end(), std::ostream_iterator<int>(cout, " "));
    cout << endl;
    std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
    cout << endl;
}

它输出:

-2 0 -1 0 1 2 
0 0 1 2 
-2 0 -1 0 1 2 

我期待看到这样的东西:

-2 0 -1 0 1 2 
0 0 1 2 
0 0 1 2 ? ? ?

在哪里 ?可以是任何值。但令我惊讶的是,输入范围未被触及,并且返回值不能与(在这种情况下)std::vector::erase() 一起使用。(返回值是一个输出迭代器。)

4

5 回答 5

25

这可能是 STL 中命名最差的函数吗?

一些背景信息:在标准库(或原始 STL)中,存在三个概念,即容器、容器中的迭代器以及应用于迭代器的算法。迭代器用作范围元素的游标和访问器,但没有对容器的引用(如前所述,甚至可能没有底层容器)。

这种分离有一个很好的特性,您可以将算法应用于不属于容器的元素范围(考虑像std::istream_iteratoror之类的迭代器适配器std::ostream_iterator)或者属于容器的不考虑所有元素(std::sort( v.begin(), v.begin()+v.size()/2 )缩短容器的前半部分) )。

不利的一面是,因为算法(和迭代器)并不真正了解容器,他们无法真正修改它,他们只能修改存储的元素(这是他们可以访问的)。变异算法,喜欢std::removestd::remove_if在这个前提下工作:它们覆盖与条件不匹配的元素,有效地它们从容器中删除,但它们不修改容器,只修改包含的值,这取决于调用者在第二步擦除删除成语的:

v.erase( std::remove_if( v.begin(), v.end(), pred ),
         v.end() );

此外,对于变异算法(那些执行更改的算法),就像std::remove有一个通过添加名称来命名的非变异版本copystd::remove_copy_if. 没有一种XXXcopyYYY算法被认为会更改输入序列(尽管如果您使用别名迭代器,它们可以)。

虽然这确实不是命名 的借口,但std::remove_copy_if我希望它有助于理解一个算法在给定它的名称时做了什么:remove_if将修改范围的内容并产生一个范围,其中所有与谓词匹配的元素都已被删除(返回的范围是由算法的第一个参数到返回的迭代器形成的)。std::remove_copy_if做同样的事情,但不是修改底层序列,而是创建序列的副本,其中与谓词匹配的那些元素已被删除。即所有 * copy * 算法都等价于copy然后应用原算法(注意等价是合乎逻辑的,std::remove_copy_if只需要一个OutputIterator,这意味着它不可能复制然后遍历复制的范围应用std::remove_if

相同的推理路线可以应用于其他变异算法:reverse反转范围中的(请记住,迭代器不访问容器),将范围中reverse_copy的元素以相反的顺序复制到单独的范围中。

如果没有,是否有 STL 算法有条件地从容器中删除(移动?)元素并将它们放入另一个容器中?

STL 中没有这样的算法,但它很容易实现:

template <typename FIterator, typename OIterator, typename Pred>
FIterator splice_if( FIterator first, FIterator last, OIterator out, Pred p )
{
   FIterator result = first;
   for ( ; first != last; ++first ) {
      if ( p( *first ) ) {
         *result++ = *first;
      } else {
         *out++ = *first;
      }
   }
   return result;
}
于 2012-08-13T10:45:18.003 回答
5

是否有用于有条件地从容器中删除(移动?)元素并将它们放入另一个容器中的 STL 算法?

我能想到的最接近的事情是std::stable_partition

std::vector<int> v;
// ...
auto it = std::stable_partition(v.begin(), v.end(), pick_the_good_elements);
std::vector<int> w(std::make_move_iter(it), std::make_move_iter(v.end()));
v.erase(it, v.end());

现在v将包含“好”元素,w并将包含“坏”元素。

于 2012-08-13T08:20:14.370 回答
2

如果没有,是否有 STL 算法有条件地从容器中删除(移动?)元素并将它们放入另一个容器中?

并不真地。这个想法是允许修改算法在容器中“移动”(不是 C++ 意义上的)元素,但不能改变容器的长度。所以remove可以调用算法prepare_for_removal

顺便说一句,C++11 提供了std::copy_if,它允许您将选定的元素从一个容器复制到另一个容器,而无需使用remove_copy_if.

于 2012-08-13T04:30:02.837 回答
1

你是对的,这就是它的作用...... std::remove_copy_if 复制向量,删除任何与 pred 匹配的内容。

std::remove_if ... 在条件下删除(或者更确切地说,将事物随机化)。

于 2012-08-13T04:21:27.047 回答
0

我同意这remove不是这个函数系列的最佳名称。

但正如 Luc 所说,它的工作方式是有原因的,他提到的GoTW 项目解释了它是如何工作的。remove_if工作方式与 remove 完全相同 - 这是您所期望的。

您可能还想阅读这篇Wikibooks 文章

于 2012-08-13T04:29:54.480 回答