简体   繁体   English

vector :: erase和reverse_iterator

[英]vector::erase and reverse_iterator

I have a collection of elements in a std::vector that are sorted in a descending order starting from the first element. 我在std :: vector中有一个元素集合,它们从第一个元素开始按降序排序。 I have to use a vector because I need to have the elements in a contiguous chunk of memory. 我必须使用向量,因为我需要将元素放在连续的内存块中。 And I have a collection holding many instances of vectors with the described characteristics (always sorted in a descending order). 我有一个集合,其中包含许多具有所述特征的向量实例(总是按降序排序)。

Now, sometimes, when I find out that I have too many elements in the greater collection (the one that holds these vectors), I discard the smallest elements from these vectors some way similar to this pseudo-code: 现在,有时,当我发现我在更大的集合中有太多元素(持有这些向量的元素)时,我丢弃这些向量中的最小元素,类似于这个伪代码:

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());
         // ...  
     }
}

Now, after running this code, in what_to_delete I have a collection of iterators pointing to the original vectors that I want to remove from these vectors (overall smallest values). 现在,在运行此代码之后,在what_to_delete我有一组迭代器指向我想从这些向量中移除的原始向量(总体最小值)。 Remember, the original vectors are sorted before they hit this code, which means that for any what_to_delete[0 - n] there is no way that an iterator on position n - m would point to an element further from the beginning of the same vector than n , where m > 0 . 请记住,原始向量在它们what_to_delete[0 - n]此代码之前进行排序,这意味着对于任何what_to_delete[0 - n] ,位置n - m上的迭代器无法指向距离相同向量的开头更远的元素。 n ,其中m > 0

When erasing elements from the original vectors, I have to convert a reverse_iterator to iterator. 从原始向量中删除元素时,我必须将reverse_iterator转换为迭代器。 To do this, I rely on C++11's §24.4.1/1: 为此,我依赖于C ++ 11的§24.4.1/ 1:

The relationship between reverse_iterator and iterator is &*(reverse_iterator(i)) == &*(i- 1) reverse_iterator和迭代器之间的关系是&*(reverse_iterator(i))==&*(i-1)

Which means that to delete a vect_rit , I use: 这意味着要删除vect_rit ,我使用:

vector.erase(--vect_rit.base());

Now, according to C++11 standard §23.3.6.5/3 : 现在,根据C ++ 11标准§23.3.6.5/3

iterator erase(const_iterator position); 迭代器擦除(const_iterator位置); Effects: Invalidates iterators and references at or after the point of the erase. 效果:在擦除点处或之后使迭代器和引用无效。

How does this work with reverse_iterators? 这如何与reverse_iterators一起使用? Are reverse_iterators internally implemented with a reference to a vector's real beginning ( vector[0] ) and transforming that vect_rit to a classic iterator so then erasing would be safe? reverse_iterators是在内部实现的,是对向量的真实开始( vector[0] )的引用,并将该vect_rit转换为经典迭代器,那么擦除是否安全? Or does reverse_iterator use rbegin() (which is vector[vector.size()] ) as a reference point and deleting anything that is further from vector's 0-index would still invalidate my reverse iterator? 或者reverse_iterator使用rbegin()( vector[vector.size()] )作为参考点并删除任何远离vector 0-index的东西仍会使我的反向迭代器无效?

Edit: 编辑:

Looks like reverse_iterator uses rbegin() as its reference point. 看起来像reverse_iterator使用rbegin()作为其参考点。 Erasing elements the way I described was giving me errors about a non-deferenceable iterator after the first element was deleted. 以我描述的方式擦除元素在删除第一个元素后给出了关于非可引用迭代器的错误。 Whereas when storing classic iterators (converting to const_iterator ) while inserting to what_to_delete worked correctly. 而在插入what_to_delete正确地存储经典迭代器(转换为const_iterator )时。

Now, for future reference, does The Standard specify what should be treated as a reference point in case of a random-access reverse_iterator? 现在,为了将来参考,标准是否指定在随机访问reverse_iterator的情况下应该将哪些视为参考点? Or this is an implementation detail? 或者这是一个实现细节?

Thanks! 谢谢!

From a standardese point of view (and I'll admit, I'm not an expert on the standard): From §24.5.1.1: 从一个独立的角度来看(我承认,我不是该标准的专家):从§24.5.1.1开始:

namespace std {
    template <class Iterator>
    class reverse_iterator ...
    {
        ...
            Iterator base() const; // explicit
        ...
        protected:
            Iterator current;
        ...
    };
}

And from §24.5.1.3.3: 从§24.5.1.3.3开始:

Iterator base() const; // explicit
    Returns: current.

Thus it seems to me that so long as you don't erase anything in the vector before what one of your reverse_iterator s points to, said reverse_iterator should remain valid. 因此,在我看来,只要你没有在你的reverse_iterator指向的内容之前删除vector任何内容, reverse_iterator应保持有效。

Of course, given your description, there is one catch: if you have two contiguous elements in your vector that you end up wanting to delete, the fact that you vector.erase(--vector_rit.base()) means that you've invalidated the reverse_iterator "pointing" to the immediately preceeding element, and so your next vector.erase(...) is undefined behavior. 当然,根据你的描述,有一个问题:如果你的向量中有两个连续的元素,你最终想要删除,你的vector.erase(--vector_rit.base())意味着你已经使reverse_iterator “指向”前一个元素无效,因此您的下一个vector.erase(...)是未定义的行为。

Just in case that's clear as mud, let me say that differently: 为了防止这种情况显而易见,让我用不同的方式说:

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);

In practice, I suspect that you'll run into two possible implementations: more commonly, the underlying iterator will be little more than a (suitably wrapped) raw pointer, and so everything will work perfectly happily. 在实践中,我怀疑你会遇到两种可能的实现:更常见的是,底层iterator只是一个(适当包装的)原始指针,所以一切都会很愉快地工作。 Less commonly, the iterator might actually try to track invalidations/perform bounds checking (didn't Dinkumware STL do such things when compiled in debug mode at one point?), and just might yell at you. 不太常见的是,迭代器实际上可能会尝试跟踪失效/执行边界检查(Dinkumware STL在调试模式下一次编译时没有这样做吗?),并且可能会对你大喊大叫。

In the question you have already quoted exactly what the standard says a reverse_iterator is: 在这个问题中,您已经准确引用了标准所说的reverse_iterator

The relationship between reverse_iterator and iterator is &*(reverse_iterator(i)) == &*(i- 1) reverse_iterator和迭代器之间的关系是&*(reverse_iterator(i))==&*(i-1)

Remember that a reverse_iterator is just an 'adaptor' on top of the underlying iterator ( reverse_iterator::current ). 请记住, reverse_iterator只是底层迭代器( reverse_iterator::current )之上的“适配器”。 The 'reference point', as you put it, for a reverse_iterator is that wrapped iterator, current . 正如你所说,对于reverse_iterator来说,'参考点'是包装的迭代器, current All operations on the reverse_iterator really occur on that underlying iterator. reverse_iterator上的所有操作都确实发生在底层迭代器上。 You can obtain that iterator using the reverse_iterator::base() function. 您可以使用reverse_iterator::base()函数获取该迭代器。

If you erase --vect_rit.base() , you are in effect erasing --current , so current will be invalidated. 如果擦除--vect_rit.base() ,则实际上是擦除 - --current ,因此current将无效。

As a side note, the expression --vect_rit.base() might not always compile. 作为旁注,表达式--vect_rit.base()可能并不总是编译。 If the iterator is actually just a raw pointer (as might be the case for a vector ), then vect_rit.base() returns an rvalue (a prvalue in C++11 terms), so the pre-decrement operator won't work on it since that operator needs a modifiable lvalue. 如果迭代器实际上只是一个原始指针(可能是vector的情况),那么vect_rit.base()返回一个rvalue(以C ++ 11术语表示的prvalue),因此pre-decrement运算符将不起作用因为该运算符需要一个可修改的左值。 See "Item 28: Understand how to use a reverse_iterator 's base iterator " in "Effective STL" by Scott Meyers. 请参阅Scott Meyers的“有效STL”中的“第28项:了解如何使用reverse_iterator的基础iterator ”。 (an early version of the item can be found online in "Guideline 3" of http://www.drdobbs.com/three-guidelines-for-effective-iterator/184401406 ). (该项目的早期版本可在http://www.drdobbs.com/three-guidelines-for-effective-iterator/184401406的 “准则3”中在线找到 )。

You can use the even uglier expression, (++vect_rit).base() , to avoid that problem. 您可以使用偶数(++vect_rit).base()表达式(++vect_rit).base()来避免该问题。 Or since you're dealing with a vector and random access iterators: vect_rit.base() - 1 或者因为你正在处理向量和随机访问迭代器: vect_rit.base() - 1

Either way, vect_rit is invalidated by the erase because vect_rit.current is invalidated. 无论哪种方式, vect_rit都会使vect_rit无效,因为vect_rit.current无效。

However, remember that vector::erase() returns a valid iterator to the new location of the element that followed the one that was just erased. 但是,请记住, vector::erase()会将有效迭代器返回到刚刚擦除的元素的新位置。 You can use that to 're-synchronize' vect_rit : 您可以使用它来“重新同步” vect_rit

vect_rit = vector_type::reverse_iterator( vector.erase(vect_rit.base() - 1));

The reverse_iterator , just like the normal iterator , points to a certain position in the vector. reverse_iterator与正常iterator ,指向向量中的某个位置。 Implementation details are irrelevant, but if you must know, they both are (in a typical implementation) just plain old pointers inside. 实现细节是无关紧要的,但是如果你必须知道,它们(在一个典型的实现中)都只是里面的普通旧指针。 The difference is the direction. 不同的是方向。 The reverse iterator has its + and - reversed wrt the regular iterator (and also ++ and -- , > and < etc). 反向迭代器有+-反转常规迭代器(以及++-->< etc)。

This is interesting to know, but doesn't really imply an answer to the main question. 这很有趣,但并不能真正暗示对主要问题的回答。

If you read the language carefully, it says: 如果您仔细阅读该语言,它会说:

Invalidates iterators and references at or after the point of the erase. 在擦除点处或之后使迭代器和引用无效。

References do not have a built-in sense of direction. 参考文献没有内置的方向感。 Hence, the language clearly refers to the container's own sense of direction. 因此,语言清楚地指的是容器自身的方向感。 Positions after the point of the erase are those with higher indices. 擦除点之后的位置是具有较高索引的位置。 Hence, the iterator's direction is irrelevant here. 因此,迭代器的方向在这里是无关紧要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM