std::next with n > std::d istance(it, c.end())



我不想使用std::distance,因为它会计算从迭代器到终点的整个距离。但是我需要确保从迭代器到最后有 N 个或更多元素。所以我正在使用下一个代码:

if (std::next(it, n) != c.end()) // c is a std::multimap
{
    /// my logic
}

一切都很棒,并且与我的编译器(g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9))一起工作,但我有疑问。在文档(cpprefenece.com&&cplusplus.com)中,我找不到有关n > std::distance(it , c.end())或任何其他特殊情况的任何信息。所以。我的代码安全吗?或者我应该写我自己的nextIfPossible

如果

distance(it, c.end())小于n,则next(it, n)是未定义的行为。

[C++14: 5.7/5] 如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则计算不应产生溢出;否则,行为是未定义的。

有关详细信息,请参阅此处:非取消引用迭代器是否超过了数组未定义行为的"一个过去结束"迭代器?

您必须编写nextIfPossible否则您的代码是未定义的。也就是说,由于我猜这是一个随机访问迭代器,您会发现在必须执行边界检查的情况下,使用索引进行基准测试比使用迭代器更快:https://stackoverflow.com/a/37299761/2642059

所以我建议甚至不要打扰迭代器或nextIfPossible而只是使用索引并根据大小检查它。

根据标准 §24.4.4/p3 和 p6 迭代器操作 [iterator.operations](强调我的):

template <class InputIterator, class Distance>
constexpr void advance(InputIterator& i, Distance n);

2 要求:n 仅对双向和随机为负数 访问迭代器。

3效果:递增(或负 n 的递减) 迭代器引用 i by n。

template <class InputIterator>
constexpr InputIterator next(InputIterator x,
typename std::iterator_traits<InputIterator>::difference_type n = 1);

6效果:相当于:advance(x, n); return x;

因此,

没有边界检查,因此如果输入n大于 std::distance(it , c.end()),则可能会导致未定义的行为。

libstd++RB_Tree::iterator增量运算符的实现可确保它不会将迭代器返回到未定义的内存位置:

static _Rb_tree_node_base*
  local_Rb_tree_increment(_Rb_tree_node_base* __x) throw ()
  {
    if (__x->_M_right != 0)
      {
        __x = __x->_M_right;
        while (__x->_M_left != 0)
          __x = __x->_M_left;
      }
    else
      {
        _Rb_tree_node_base* __y = __x->_M_parent;
        while (__x == __y->_M_right)
          {
            __x = __y;
            __y = __y->_M_parent;
          }
        if (__x->_M_right != __y)
          __x = __y;
      }
    return __x;
  }

但对于std::vector来说并非如此:

#include <iostream>
#include <vector>
#include <iterator>
int main() {
  std::vector<int> vec = {1,2};
  auto it = vec.begin();
  it = std::next(it, 5);
  if (it != vec.end()) {
    std::cout << "Not end..go on" << std::endl;
  }
  return 0;
}

这将继续打印消息。

因此,

由于容器之间的行为并不相同,因此对于基于 map 的容器,您不应依赖 std::next 来实现其当前正确行为。

最新更新