为什么 std::remove_copy_if() 实际上没有删除?
why doesn't std::remove_copy_if() actually remove?
这可能是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()一起使用。(返回值是一个输出迭代器。
这可能是 STL 中最糟糕的命名函数吗?
一些背景信息:在标准库(或原始STL)中,有三个概念,容器,迭代器到那些容器和应用于迭代器的算法。迭代器充当范围元素的游标和访问器,但没有对容器的引用(如前所述,甚至可能没有基础容器)。
这种分离有一个很好的功能,你可以将算法应用于不属于容器的元素范围(考虑迭代器适配器,如std::istream_iterator
或std::ostream_iterator
),或者属于容器不考虑所有元素(std::sort( v.begin(), v.begin()+v.size()/2 )
缩短容器的前半部分)。
不利的一面是,由于算法(和迭代器)并不真正了解容器,因此它们无法真正修改它,它们只能修改存储的元素(这是它们可以访问的元素)。像std::remove
或std::remove_if
这样的突变算法在这个前提下工作:它们覆盖与条件不匹配的元素,有效地将它们从容器中删除,但它们不会修改容器,只修改包含的值,这取决于调用者在擦除删除习惯用法的第二步中:
v.erase( std::remove_if( v.begin(), v.end(), pred ),
v.end() );
此外,对于变异算法(执行更改的算法),例如std::remove
有一个非变异版本,通过在名称中添加copy
来命名:std::remove_copy_if
.XXXcopyYYY
算法都不会被视为更改输入序列(尽管如果使用混叠迭代器,它们可以更改
虽然这真的不是命名std::remove_copy_if
的借口,但我希望它有助于理解算法的名称:remove_if
将修改范围的内容并生成一个范围,其中所有与谓词匹配的元素都已删除(返回的范围是由算法的第一个参数形成的返回迭代器)。 std::remove_copy_if
执行相同的操作,但不是修改基础序列,而是创建序列的副本,其中与谓词匹配的元素已被删除。也就是说,所有 *copy* 算法都等效于复制然后应用原始算法(注意等价是合乎逻辑的,std::remove_copy_if
只需要一个输出迭代器,这意味着它不可能复制然后遍历复制的范围应用 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;
}
是否有一种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
将包含"坏"元素。
如果没有,是否有一种 STL 算法可以有条件地从容器中删除(移动?)元素并将它们放入另一个容器中?
没有。这个想法是允许修改算法在容器中"移动"(不是C++意义上的)元素,但不能改变容器的长度。因此,remove
算法可以称为prepare_for_removal
。
顺便说一下,C++11 提供了 std::copy_if,它允许您将选定的元素从一个容器复制到另一个容器,而无需与 remove_copy_if
玩有趣的逻辑游戏。
你是对的,这就是它的作用...std::remove_copy_if 复制矢量,删除与 pred 匹配的任何内容。
标准::remove_if ...有条件地删除(或者更确切地说,打乱东西)。
我同意remove
不是这个函数系列的最佳名称。
但正如 Luc 所说,它的工作方式是有原因的,他提到的 GoTW 项目解释了它是如何工作的。 remove_if
的工作方式与删除完全相同 - 这是您所期望的。
您可能还想阅读这篇维基教科书文章。
- 将数组的地址分配给变量并删除
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C/C++编译器通常会删除重复的库吗
- 从链接列表c++中删除一个项目
- C++如何通过用户输入删除列表元素
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 使用函数"remove"删除重复元素
- 如何从多映射中删除特定的重复项
- 运算符C++ "delete []"仅删除 2 个前值
- 删除指向指针的指针是运行时错误吗
- 将指针设置为"nullptr"并不能防止双重删除?
- 为什么我的删除节点函数实际上没有删除节点?
- 删除节点方法实际上不会删除二叉搜索树中的节点.C++
- 删除未使用新表达式构建的对象实际上可以吗?
- 在向量上调用 clear() 实际上不会删除 data() 中的数据?
- 为什么 std::remove_copy_if() 实际上没有删除?
- C++和 Sqlite DELETE 查询实际上不会从数据库文件中删除该值