【问题标题】:How do I erase a reverse_iterator from an stl data structure?如何从 stl 数据结构中删除 reverse_iterator?
【发布时间】:2010-09-29 02:08:44
【问题描述】:

由于某种原因,以下代码失败。您不能简单地使用它的 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。它想要真实的东西。

【问题讨论】:

    标签: c++ stl iterator


    【解决方案1】:

    使用迭代器本身调用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(或类似的函数)。

    【讨论】:

    • hmm 擦除不需要 reverse_iterators
    【解决方案2】:

    显然,解决方案是 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 中

    回到工作岗位并在 Visual Studio 中尝试此操作,我发现上述解决方案不太奏效。 “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;
            }
        }   
    
    }
    

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

    【讨论】:

    • 这总结了我生命的最后几个小时:)
    • 我认为这是因为您可以将一个指向数组末尾之后,但不能指向数组开头之前的一个。所以如果 .rend() == i ,那么 i.base() == .begin() ,而不是 .begin()-1 (这将是 UB)。所以它总是一次性的,但是在取消引用时,它会返回 *--.base()
    • 上述标识是为了确保反向迭代器可以在期望迭代器范围指定半开区间 [first, last) 的所有标准位置工作。如果不是这种情况,反向迭代器在 std 算法中将毫无用处,因为 'end' 将被取消引用并且 'begin' 将被跳过。
    • 是的,它也为这个问题提供了一个合乎逻辑的观点。如果您考虑一下,就像现在一样,基础完全从 .end() 到 .begin(),而不是从 --.end() 到 --.begin(),这不会是完全相反的在范围内迭代。
    • std::set::erase 返回迭代器这一事实是微软的扩展。
    【解决方案3】:

    当您使用反向迭代器进行迭代并希望使用 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);
    

    【讨论】:

      【解决方案4】:

      1 来自map::erase,我们知道它只需要iterator

      2 来自reverse_iterator::base,我们知道&amp;*(reverse_iterator ( i ) ) == &amp;*( i – 1 ).

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

                  r_v+1            r_v          r_v-1
                 current-2      current-1      current
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-25
        • 2021-06-30
        • 2015-11-18
        • 2012-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多