Std:: next с n> std:: distance (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?

Ответ 1

В соответствии со стандартом §24.4.4/p3 и p6 Операции итератора [итераторы.операций] (Акцент Mine):

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

2 Требуется: n должно быть отрицательным только для двунаправленного и случайного итераторы доступа.

3 Эффекты: Приращения (или уменьшения для отрицательного n) ссылка итератора я на n.

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

6 Эффекты: эквивалентны: advance(x, n); return x;

Следовательно, нет связанной проверки, и поэтому вы можете привести к поведению undefined, если вход n больше, чем std::distance(it , c.end()).

Ответ 2

next(it, n) - неопределенное поведение, если distance(it, c.end()) меньше n.

[С++ 14: 5.7/5] Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение не определено.

См. Здесь для получения дополнительной информации. Не именованы ли разыменованные итераторы мимо "одного прошедшего конца" итератора неопределенного поведения массива?

Вы должны написать nextIfPossible или ваш код не определен. Тем не менее, поскольку я предполагаю, что это итератор с произвольным доступом, вы найдете работу с индексами быстрее, чем работа с итераторами, в случае, если необходимо провести проверку границ: fooobar.com/questions/4269/...

Поэтому я бы порекомендовал даже не беспокоиться об итераторах или nextIfPossible но просто использовал индекс и проверял его на размер.

Ответ 3

Реализация оператора приращения RB_Tree::iterator в libstd++ гарантирует, что он не вернет итератор в ячейку памяти undefined:

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

Это будет продолжаться при печати сообщения.

Итак, поскольку поведение не одинаково для контейнеров, вы не должны зависеть от std::next от его текущего правильного поведения для контейнеров на основе map.