std::remove with vector::erase和未定义行为 在整个Web上我看到人们使用的是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; }

std::remove with vector::erase和未定义行为 在整个Web上我看到人们使用的是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; },c++,algorithm,c++11,vector,stl,C++,Algorithm,C++11,Vector,Stl,这在一般情况下效果很好std::remove(和remove\u if)会将要删除的元素复制(或在C++11中使用移动语义)到向量的末尾,因此我们前面示例中的向量现在如下所示: {0,1,2,3,4,6,7,8,9,5} 元素5加粗,因为它已移动到末尾 现在,std::remove将返回一个迭代器,然后在erase中使用该迭代器清除元素。很好 但是下面的例子呢? 第一个、最后一个是 迭代器指定要删除的向量]内的范围:[first,last)。即,范围包括first和last之间的所有元素,包括f

这在一般情况下效果很好
std::remove
(和
remove\u if
)会将要删除的元素复制(或在C++11中使用移动语义)到向量的末尾,因此我们前面示例中的向量现在如下所示:

{0,1,2,3,4,6,7,8,9,5}

元素5加粗,因为它已移动到末尾

现在,
std::remove
将返回一个迭代器,然后在
erase
中使用该迭代器清除元素。很好

但是下面的例子呢?
第一个、最后一个是

迭代器指定要删除的向量]内的范围:
[first,last)
。即,范围包括
first
last
之间的所有元素,包括first指向的元素,但不包括
last
指向的元素。 成员类型
iterator
const_iterator
是指向元素的随机访问迭代器类型

vector.erase(vector.end(),vector.end())
未定义的行为也是如此? 以下是《快速参考》中有关异常安全的内容:

如果删除的元素包括容器中的最后一个元素,则不会抛出异常(无抛出保证)。 否则,将保证容器以有效状态结束(基本保证)。 无效的
位置
范围
会导致未定义的行为

所以,答案,至少在我看来是“是的”,并且似乎支持它

因此,常见的习语是错误的吗? 假设它是未定义的行为,那么对
remove
的任何调用都可能返回到
vector.end()
的迭代器,在调用
vector.erase
之前应该检查迭代器,对空向量调用remove似乎返回
vector.end
:()

#包括
#包括
#包括
使用名称空间std;
int main(){
媒介粘虫;
auto anIter=std::remove(myInts.begin(),myInts.end(),5);
if(anIter==myInts.end())
标准::cout
vector.erase(vector.end(),vector.end())的行为是否未定义

不,因为你的演讲旁边有一句话:

迭代器指定要删除的向量]内的范围:[first,last]。即,该范围包括first和last之间的所有元素,包括first指向的元素,但不包括last指向的元素

因此,
vector.erase(vector.end(),vector.end())
不会尝试擦除
vector.end()
,因为它是由参数
last
指向的

诚然,这一定义是含糊不清的,这些陈述可能被解释为相互矛盾的。本标准未使用引用的措辞

24.2.1/7大多数在数据结构上运行的库算法模板都有使用范围的接口。范围是一对 指定计算开始和结束的迭代器的名称。 范围
[i,i)
是一个空范围;通常,范围
[i,j)
指数据结构中以元素开头的元素 由
i
指向,但不包括指向的元素 通过
j

我的

此外,您引用的
erase
说明不是本标准中的规范性文本。本标准规定(表100):

a.擦除(q1,q2)

效果:擦除[q1,q2]范围内的元素


这并不要求
q1
是可取消引用的。如果[q1,q2]是一个空范围(根据24.2.1/7),那么该范围内没有元素,因此没有任何元素被删除。

我认为在您的引用中更重要的是:

迭代器指定要删除的向量]内的范围: [第一个,最后一个)。即,范围包括第一个和第二个之间的所有元素 最后,包括第一个指向的元素,但不包括第一个指向的元素 由last指向。成员类型迭代器和常量迭代器是随机的 访问指向元素的迭代器类型

正如我们在评论中所发现的,此引用是不正确的。在
(v.end,v.end)
的情况下,这不会违反规则,但在

#include <vector>

int main()
{
    std::vector<int> v = { 1, 2, 3 };

    v.erase( v.begin(), v.begin());
}
#包括
int main()
{
向量v={1,2,3};
v、 擦除(v.begin(),v.begin());
}
因为这句话与

范围包括(…),包括所指的元素 v、 begin()但不是v.begin()所指的那一个

不能是有效的语句

§23.2.2序列容器要求表100中的C++标准n3337规定

a.erase(q1,q2)
返回
iterator
。注意:

要求:对于vector和deque,T应是可移动和可分配的。效果: 擦除[q1,q2]范围内的元素。

这就是它在§24.2.1/7迭代器要求中对范围
[i,j)
的说明

库中大多数对数据进行操作的算法模板 结构具有使用范围的接口。范围是一对 指定计算开始和结束的迭代器。A 范围[i,i)是一个空范围;通常,范围[i,j]指的是 数据结构中以 i和之前,但不包括j所指的元素。范围[i,j) 当且仅当j可从i到达时有效 无法将库中的函数应用于无效范围 未定义

这样就可以回答你的问题了

但是下面的例子呢

cplusplus.com是
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;
}
iterator erase (const_iterator first, const_iterator last);
#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())
#include <vector>

int main()
{
    std::vector<int> v = { 1, 2, 3 };

    v.erase( v.begin(), v.begin());
}