如何从 stl 数据结构中删除反向迭代器?

发布于 2024-07-11 05:46:57 字数 816 浏览 4 评论 0原文

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

For some reason the following code fails. You can't simply erase a reverse_iterator by using its base() method.

#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;
    }

}

How does one go about correctly doing the above? Given a reverse_iterator that corresponds to something you want to erase, how do you erase it?

Note, erase won't take reverse_iterators unfortunately. It wants the real thing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

罗罗贝儿 2024-07-18 05:46:57

显然,解决方案是 base() 返回的是 1。 以下恒等式适用于反向迭代器:

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

或者换句话说,反向迭代器始终是其基础的常规迭代器的一次传递。 不知道为什么。

在 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;
        }
    }   

}

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

Apparently the solution is what base() returns is 1 off. The following identity holds for a reverse_iterator:

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

Or in other words, the reverse_iterator is always one pass the regular iterator it is the base of. Not sure why.

In GCC

Simply change

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

to

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

I'm definitely curious though as to why the identity above makes sense.

In Visual Studio

Coming back into work and trying this in visual studio, I see the above solution doesn't quite work. The "nextIter" becomes invalid on the erase. Instead, you need to save away the temporary from the erase to get the next iterator instead of keeping around a nextIter like above.

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

So the final solution is

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

}

Note, associative containers do not return an iterator from erase. So this solution wouldn't work for map, multimap, etc.

你是我的挚爱i 2024-07-18 05:46:57

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

When you iterate with a reverse iterator and want to use base() to modify its container, always keep in mind that a reverse_iterator is always based on the next iterator from the original order. It's a little unintuitive but it actually makes the code simpler:

#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;
    }
}

In this example, there is not need to keep a "next" iterator since the base iterator is not invalidated! (We do need it when dealing with normal iterators.)

The behavior of reverse iterators creates weird off-by-one difficulties when treating with a single item but in facts it simplify ranges:

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

is using exactly the same objects (in reverse order) as

iValue = find(riBegin, riEnd, value);
请远离我 2024-07-18 05:46:57

1 来自 map::erase,我们知道它只需要迭代器;

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

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

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

1 from map::erase, we know it only takes iterator;

2 from reverse_iterator::base, we know &*(reverse_iterator ( i ) ) == &*( i – 1 ).

Therefore, you can erase(--r_v.base()) to erase the element pointed by "r_v" (and "current-1"):

            r_v+1            r_v          r_v-1
           current-2      current-1      current
画中仙 2024-07-18 05:46:57

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

Call erase with the iterator itself (no need to use 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;
        }
    }
}

Also, you don't need that separate "next" iterator (see above changes). An even better way to do this is to use std::remove_if (or a function like it).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文