[英]vector::erase and reverse_iterator
我在std :: vector中有一个元素集合,它们从第一个元素开始按降序排序。 我必须使用向量,因为我需要将元素放在连续的内存块中。 我有一个集合,其中包含许多具有所述特征的向量实例(总是按降序排序)。
现在,有时,当我发现我在更大的集合中有太多元素(持有这些向量的元素)时,我丢弃这些向量中的最小元素,类似于这个伪代码:
grand_collection: collection that holds these vectors
T: type argument of my vector
C: the type that is a member of T, that participates in the < comparison (this is what sorts data before they hit any of the vectors).
std::map<C, std::pair<T::const_reverse_iterator, std::vector<T>&>> what_to_delete;
iterate(it = grand_collection.begin() -> grand_collection.end())
{
iterate(vect_rit = it->rbegin() -> it->rend())
{
// ...
what_to_delete <- (vect_rit->C, pair(vect_rit, *it))
if (what_to_delete.size() > threshold)
what_to_delete.erase(what_to_delete.begin());
// ...
}
}
现在,在运行此代码之后,在what_to_delete
我有一组迭代器指向我想从这些向量中移除的原始向量(总体最小值)。 请记住,原始向量在它们what_to_delete[0 - n]
此代码之前进行排序,这意味着对于任何what_to_delete[0 - n]
,位置n - m
上的迭代器无法指向距离相同向量的开头更远的元素。 n
,其中m > 0
。
从原始向量中删除元素时,我必须将reverse_iterator转换为迭代器。 为此,我依赖于C ++ 11的§24.4.1/ 1:
reverse_iterator和迭代器之间的关系是&*(reverse_iterator(i))==&*(i-1)
这意味着要删除vect_rit
,我使用:
vector.erase(--vect_rit.base());
现在,根据C ++ 11标准§23.3.6.5/3
:
迭代器擦除(const_iterator位置); 效果:在擦除点处或之后使迭代器和引用无效。
这如何与reverse_iterators一起使用? reverse_iterators是在内部实现的,是对向量的真实开始( vector[0]
)的引用,并将该vect_rit转换为经典迭代器,那么擦除是否安全? 或者reverse_iterator使用rbegin()( vector[vector.size()]
)作为参考点并删除任何远离vector 0-index的东西仍会使我的反向迭代器无效?
编辑:
看起来像reverse_iterator使用rbegin()作为其参考点。 以我描述的方式擦除元素在删除第一个元素后给出了关于非可引用迭代器的错误。 而在插入what_to_delete
正确地存储经典迭代器(转换为const_iterator
)时。
现在,为了将来参考,标准是否指定在随机访问reverse_iterator的情况下应该将哪些视为参考点? 或者这是一个实现细节?
谢谢!
从一个独立的角度来看(我承认,我不是该标准的专家):从§24.5.1.1开始:
namespace std {
template <class Iterator>
class reverse_iterator ...
{
...
Iterator base() const; // explicit
...
protected:
Iterator current;
...
};
}
从§24.5.1.3.3开始:
Iterator base() const; // explicit
Returns: current.
因此,在我看来,只要你没有在你的reverse_iterator
指向的内容之前删除vector
任何内容, reverse_iterator
应保持有效。
当然,根据你的描述,有一个问题:如果你的向量中有两个连续的元素,你最终想要删除,你的vector.erase(--vector_rit.base())
意味着你已经使reverse_iterator
“指向”前一个元素无效,因此您的下一个vector.erase(...)
是未定义的行为。
为了防止这种情况显而易见,让我用不同的方式说:
std::vector<T> v=...;
...
// it_1 and it_2 are contiguous
std::vector<T>::reverse_iterator it_1=v.rend();
std::vector<T>::reverse_iterator it_2=it_1;
--it_2;
// Erase everything after it_1's pointee:
// convert from reverse_iterator to iterator
std::vector<T>::iterator tmp_it=it_1.base();
// but that points one too far in, so decrement;
--tmp_it;
// of course, now tmp_it points at it_2's base:
assert(tmp_it == it_2.base());
// perform erasure
v.erase(tmp_it); // invalidates all iterators pointing at or past *tmp_it
// (like, say it_2.base()...)
// now delete it_2's pointee:
std::vector<T>::iterator tmp_it_2=it_2.base(); // note, invalid iterator!
// undefined behavior:
--tmp_it_2;
v.erase(tmp_it_2);
在实践中,我怀疑你会遇到两种可能的实现:更常见的是,底层iterator
只是一个(适当包装的)原始指针,所以一切都会很愉快地工作。 不太常见的是,迭代器实际上可能会尝试跟踪失效/执行边界检查(Dinkumware STL在调试模式下一次编译时没有这样做吗?),并且可能会对你大喊大叫。
在这个问题中,您已经准确引用了标准所说的reverse_iterator
:
reverse_iterator和迭代器之间的关系是&*(reverse_iterator(i))==&*(i-1)
请记住, reverse_iterator
只是底层迭代器( reverse_iterator::current
)之上的“适配器”。 正如你所说,对于reverse_iterator
来说,'参考点'是包装的迭代器, current
。 reverse_iterator
上的所有操作都确实发生在底层迭代器上。 您可以使用reverse_iterator::base()
函数获取该迭代器。
如果擦除--vect_rit.base()
,则实际上是擦除 - --current
,因此current
将无效。
作为旁注,表达式--vect_rit.base()
可能并不总是编译。 如果迭代器实际上只是一个原始指针(可能是vector
的情况),那么vect_rit.base()
返回一个rvalue(以C ++ 11术语表示的prvalue),因此pre-decrement运算符将不起作用因为该运算符需要一个可修改的左值。 请参阅Scott Meyers的“有效STL”中的“第28项:了解如何使用reverse_iterator
的基础iterator
”。 (该项目的早期版本可在http://www.drdobbs.com/three-guidelines-for-effective-iterator/184401406的 “准则3”中在线找到 )。
您可以使用偶数(++vect_rit).base()
表达式(++vect_rit).base()
来避免该问题。 或者因为你正在处理向量和随机访问迭代器: vect_rit.base() - 1
无论哪种方式, vect_rit
都会使vect_rit
无效,因为vect_rit.current
无效。
但是,请记住, vector::erase()
会将有效迭代器返回到刚刚擦除的元素的新位置。 您可以使用它来“重新同步” vect_rit
:
vect_rit = vector_type::reverse_iterator( vector.erase(vect_rit.base() - 1));
reverse_iterator
与正常iterator
,指向向量中的某个位置。 实现细节是无关紧要的,但是如果你必须知道,它们(在一个典型的实现中)都只是里面的普通旧指针。 不同的是方向。 反向迭代器有+
和-
反转常规迭代器(以及++
和--
, >
和<
etc)。
这很有趣,但并不能真正暗示对主要问题的回答。
如果您仔细阅读该语言,它会说:
在擦除点处或之后使迭代器和引用无效。
参考文献没有内置的方向感。 因此,语言清楚地指的是容器自身的方向感。 擦除点之后的位置是具有较高索引的位置。 因此,迭代器的方向在这里是无关紧要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.