代码之家  ›  专栏  ›  技术社区  ›  Doug T.

如何从stl数据结构中删除reverse_iterator?

  •  15
  • Doug T.  · 技术社区  · 16 年前

    由于某种原因,以下代码失败。您不能简单地使用其base()方法擦除reverse_iterator。

    #include <set>
    #include <iostream>
    
    int main()
    {
        std::set<int> setOfInts;
        setOfInts.insert(1);
        setOfInts.insert(2);
        setOfInts.insert(3);
    
        std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
        std::set<int>::reverse_iterator nextRevIter = setOfInts.rbegin();
        ++nextIter;
    
        while ( rev_iter != setOfInts.rend())
        {
            // Find 3 and try to erase
            if (*rev_iter == 3)
            {
                // SEGFAULT HERE
                setOfInts.erase( rev_iter.base());
            }
            rev_iter = nextRevIter;
            ++nextRevIter;
        }
    
    }
    

    如何正确地做上述事情?给定一个与您要擦除的内容相对应的reverse_iterator,您如何擦除它?

    注意,遗憾的是,erase不会使用reverse_iterators。它想要真实的东西。

    4 回复  |  直到 16 年前
        1
  •  18
  •   Doug T.    16 年前

    显然,解决方案是base()返回的值为1。以下标识适用于reverse_iterator:

    &*(reverse_iterator(i)) == &*(i - 1) 
    

    或者换句话说,reverse_iterator始终是它所基于的常规迭代器的一次传递。不知道为什么。

    在GCC中

    只需更改

            // SEGFAULT HERE
            setOfInts.erase( rev_iter.base());
    

            // WORKS!
            setOfInts.erase( --rev_iter.base());
    

    不过,我确实很好奇为什么上面的身份是有意义的。

    在Visual Studio中

    回到工作中,在visualstudio中尝试一下,我发现上面的解决方案不太奏效。“nextIter”在擦除时无效。相反,您需要从擦除中保存临时值以获取下一个迭代器,而不是像上面那样保留一个nextIter。

      set<int>::iterator tempIter = setOfInts.erase(--rev_iter.base());
      rev_iter = setOfInts.erase(tempIter);
    

    所以最终的解决方案是

    int main()
    {
        using namespace std;
    
        set<int> setOfInts;
        setOfInts.insert(1);
        setOfInts.insert(2);
        setOfInts.insert(3);
    
        set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
    
        while ( rev_iter != setOfInts.rend())
        {
            // Find 3 and try to erase
            if (*rev_iter == 3)
            {
                cout << "Erasing : " << *rev_iter;
                set<int>::iterator tempIter = setOfInts.erase( --rev_iter.base());
                rev_iter = set<int>::reverse_iterator(tempIter);            
            }
            else
            {
                ++rev_iter;
            }
        }   
    
    }
    

    注意,关联容器不会从擦除返回迭代器。所以这个解决方案不适用于map、multimap等。

        2
  •  3
  •   alexk7    16 年前

    当您使用反向迭代器迭代并希望使用base()修改其容器时,请始终记住,reverse_iterator始终基于原始顺序的下一个迭代器。这有点不直观,但它实际上使代码更简单:

    #include <set>
    int main()
    {
        std::set<int> setOfInts;
        setOfInts.insert(1);
        setOfInts.insert(2);
        setOfInts.insert(3);
    
        typedef std::set<int>::reverse_iterator RevIter;
    
        RevIter rev_iter = setOfInts.rbegin();
        while (rev_iter != setOfInts.rend())
        {
            // Find 3 and try to erase
            if (*rev_iter == 3)
                setOfInts.erase(--rev_iter.base());
    
            ++rev_iter;
        }
    }
    

    在这个例子中,不需要保留“下一个”迭代器,因为基本迭代器没有失效!(在处理普通迭代器时,我们确实需要它。)

    反向迭代器的行为在处理单个项目时会造成奇怪的逐一困难,但事实上它简化了范围:

    riValue = find(riEnd.base(), riBegin.base(), value);
    

    使用与以下对象完全相同的对象(顺序相反)

    iValue = find(riBegin, riEnd, value);
    
        3
  •  0
  •   YoungLearner    11 年前

    1从 map::erase 我们知道这只需要 iterator ;

    2从 reverse_iterator::base ,我们知道 &*(reverse_iterator ( i ) ) == &*( i – 1 ).

    因此,您可以擦除(--r_v.base())来擦除“r_v”(和“current-1”)指向的元素:

                r_v+1            r_v          r_v-1
               current-2      current-1      current
    
        4
  •  -3
  •   Brian    16 年前

    呼叫 erase 使用迭代器本身(无需使用 base ).

    #include <set>
    #include <iostream>
    
    int main()
    {
        std::set<int> setOfInts;
        setOfInts.insert(1);
        setOfInts.insert(2);
        setOfInts.insert(3);
    
        std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
    
        while (rev_iter != setOfInts.rend())
        {
            // Find 3 and try to erase
            if (*rev_iter == 3)
            {
                rev_iter = setOfInts.erase(rev_iter);
            }
            else
            {
                ++rev_iter;
            }
        }
    }
    

    此外,您不需要单独的“下一个”迭代器(见上面的更改)。更好的方法是使用 std::remove_if (或类似的功能)。