【发布时间】:2014-07-08 19:15:00
【问题描述】:
在整个网络上,我看到人们将erase/remove idiom 用于 C++ 向量,如下所示:
#include <vector> // the general-purpose vector container
#include <iostream>
#include <algorithm> // remove and remove_if
int main()
{
// initialises a vector that holds the numbers from 0-9.
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
也就是说,如果我想删除所有符合某些条件的元素(例如,ints 向量中的数字 5),那么我将 std::remove 或 std::remove_if 与 vector.erase 结合使用,如下所示:
vector.erase( std::remove( vector.begin(), vector.end(), <some_value>), vector.end());
总的来说,这很好用; std::remove(和 remove_if)将复制(或在 C++11 中使用移动语义)要删除的元素到向量的末尾,因此我们之前示例中的向量现在看起来像这样:
{ 0, 1, 2, 3, 4, 6, 7, 8, 9, 5 };
元素 5 加粗,因为它已移到末尾。
现在,std::remove 将返回一个迭代器,然后我们在 erase 中使用它来清除元素。不错。
但是下面的例子呢?
int main()
{
// initialises an empty vector.
std::vector<int> v = {};
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
这似乎在我运行它的所有平台上都按预期工作(不删除任何内容,不进行段错误等),但我知道仅仅因为某些东西在工作,并不意味着它不是未定义的行为。
vector.erase 的快速 reference 是这样说的(强调我的):
iterator erase (const_iterator first, const_iterator last);
first, last 是
在向量内指定范围的迭代器] 要删除:
[first,last)。即范围包括first和last之间的所有元素,包括first指向的元素,但不包括last指向的元素。 成员类型iterator和const_iterator是指向元素的随机访问迭代器类型。
vector.erase(vector.end(),vector.end()) 是未定义的行为吗?
以下是快速参考中关于异常安全的内容:
如果移除的元素包括容器中的最后一个元素,则不会抛出异常(保证不抛出)。 否则,保证容器以有效状态结束(基本保证)。 无效的
position或range会导致未定义的行为。
所以,至少在我看来答案是“是”,this StackOverflow answer 似乎支持它。
所以,习语有错吗?
假设它是未定义的行为,那么任何对 remove 的调用都可能返回一个迭代器到 vector.end(),在调用 vector.erase 之前应该检查它,并且在空向量上调用 remove 似乎返回 vector.end: (@ 987654324@)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> myInts;
auto anIter = std::remove(myInts.begin(),myInts.end(),5);
if (anIter == myInts.end())
std::cout << "iterator = myInts.end()";
}
最后,我的问题:
实际的删除/擦除习语应该是这样吗?
auto endOfRangeIterator = std::remove(vector.begin(), vector.end(), <value>);
if (endOfRangeIterator != vector.end())
vector.erase(endOfRangeIterator, vector.end())
【问题讨论】:
-
不,空范围也可以。
[first, first)是一个空集。 -
@privatedatapublicchannel2:实际上,引用不正确。在
first==last的情况下,引用自相矛盾,指定一个元素被删除,但没有被删除。这就解释了为什么 AndyG 开始相信这是未定义的行为。 -
@privatedatapublicchannel2:不一样。标准实际上并没有说
i指向的元素包含在范围内。它只是说这是范围的开始。但是,它确实说j指向的元素是not 包括在内的。此外,传递给erase的第二个迭代器不一定是结束迭代器,所以如果first==last,这并不一定意味着第一个指向结束。 -
@privatedatapublicchannel2:为了清楚起见,请阅读 cplusplus.com 的报价,将“last”替换为“first”。 "包括first指向的元素,但不包括first指向的元素"。在这种情况下,该陈述显然是矛盾的。
-
最终我们会erase_if 这将简化整个事情。
标签: c++ algorithm c++11 vector stl