非 void STL 擦除的安全等效项是什么?

发布于 2024-07-07 17:46:07 字数 259 浏览 6 评论 0原文

假设我有一个 hash_map 和类似的代码,

// i is an iterator
i = hash_map.erase(i)

但是 GCC 的 STL 在擦除时不会返回迭代器,而是返回一个 void。 现在的代码是否

hash_map.erase(i++)

安全(即不会使迭代器无效或发生任何其他意外或不愉快的事情)? 请注意这是一个 hash_map。

Suppose I have a hash_map and a code like

// i is an iterator
i = hash_map.erase(i)

But GCC's STL doesn't return iterator in erase, but a void. Now is a code like

hash_map.erase(i++)

safe (i.e. does not invalidate the iterator or does any other unexpected or unpleasant things)? Please note this is a hash_map.

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

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

发布评论

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

评论(3

是伱的 2024-07-14 17:46:07

是的,这是安全的,因为在擦除当前值之前,i 的值将被设置为下一个值。

根据 有关散列容器的 SGI 文档,未擦除的元素不会发生失效,甚至也没有调整大小(没有说明插入是否会导致调整大小,所以要小心,我承认这是一种可能性)——但在后一种情况下,迭代顺序将会改变。 但这并不适用于此,除非您在遍历或其他过程中不遗余力地调整容器的大小。 :-)

Yes, this is safe, because the value of i will have been set to the next value, before the current value is erased.

According to the SGI documentation about hashed containers invalidation does not occur for non-erased elements, nor even for resizing (there is no word on whether insertions cause resizing, so to be careful I admit that as a possibility)---but in the latter case, the iteration order will be changed. But this doesn't apply here, unless you go out of your way to resize the container during traversal or something. :-)

女皇必胜 2024-07-14 17:46:07

您可以封装擦除,为您使用的所有容器提供相同的接口:

namespace detail {
template<typename Container, typename R>
struct SelectErase {
  // by default, assume the next iterator is returned
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    return c.erase(where);
  }
};
// specialize on return type void
template<typename Container>
struct SelectErase<Container, void> {
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    Iterator next (where);
    ++next;
    c.erase(where);
    return next;
  }
};

template<typename I, typename Container, typename R>
SelectErase<Container,R> select_erase(R (Container::*)(I)) {
  return SelectErase<Container,R>();
}
} // namespace detail

template<typename Container, typename Iterator>
Iterator erase(Container& container, Iterator where) {
  return detail::select_erase<Iterator>(&Container::erase).erase(container, where);
}

这需要:

  1. c.erase 返回下一项的迭代器。 这就是向量、双端队列和列表的工作原理。
  2. c.erase 返回 void 并且不会使下一个迭代器无效。 这就是map、set 和(非stdlib)hash_map 的工作原理。

You can encapsulate erasing to provide the same interface for all containers you use:

namespace detail {
template<typename Container, typename R>
struct SelectErase {
  // by default, assume the next iterator is returned
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    return c.erase(where);
  }
};
// specialize on return type void
template<typename Container>
struct SelectErase<Container, void> {
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    Iterator next (where);
    ++next;
    c.erase(where);
    return next;
  }
};

template<typename I, typename Container, typename R>
SelectErase<Container,R> select_erase(R (Container::*)(I)) {
  return SelectErase<Container,R>();
}
} // namespace detail

template<typename Container, typename Iterator>
Iterator erase(Container& container, Iterator where) {
  return detail::select_erase<Iterator>(&Container::erase).erase(container, where);
}

This requires either:

  1. c.erase returns the iterator for the next item. This is how vector, deque, and list work.
  2. c.erase returns void and does not invalidate the next iterator. This is how map, set, and (non-stdlib) hash_map work.
演多会厌 2024-07-14 17:46:07

我不想在游行中下雨,但我认为你的提议并不安全。

i++ 是后递增运算符,这意味着 i 在调用擦除之后递增。 但是擦除会使所有指向被擦除元素的迭代器无效。 因此,当 i 递增时,它不再有效。

如果你幸运的话,它可能会意外地正常工作,直到有一天它不再正常工作。

据我所知,没有办法解决这个问题,但类似:

// tmp and i are both iterators
tmp = i;
++i;
hash_map.erase(tmp);

Hate to rain on the parade, but I don't think what you propose is safe.

i++ is the post-increment operator, which means i is incremented after the call to erase. But erase invalidates all iterators pointing to the element being erased. So by the time i is incremented it's not valid any more.

If you're lucky it may work correctly by accident until one day it doesn't any more.

As far as I'm aware there is no way around this but something like:

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