Определено ли это, чтобы обеспечить инвертированный диапазон для стандартных алгоритмов С++?

Рассмотрим стандартные алгоритмы, например, std::for_each.

template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);

Насколько я могу судить, на относительные состояния двух аргументов InputIterator не существует требования.

Означает ли это, что следующее технически обосновано? Или это undefined? Что я могу реально ожидать от этого?

std::vector<int> v{0,1,2,3,4};
std::for_each(
   v.begin()+3,  // range [3,0)
   v.begin(),
   [](int){}
);

geordi говорит мне:

error: функция требует допустимого диапазона итератора [__first, __last). [+ 13 отброшенных строк]

но я не могу сказать, насколько совместима эта диагностика отладки.


Я придумал этот вопрос, пытаясь педантично определить, насколько явным определяется поведение следующего:

std::vector<int> v; // <-- empty
std::for_each(      // <-- total no-op? stated or just left to implication?
   v.begin(),
   v.end(),
   [](int){}
);

Ответ 1

Стандарт явно требует, чтобы итератор last был доступен из итератора first. Это означает, что, увеличивая first, вы должны в конечном итоге нажать last.

24.1 Требования к итератору

...

6 Итератор j называется достижимым из итератора i тогда и только тогда, когда существует конечная последовательность применений выражения ++i, что делает i == j. Если j достижимо от i, они относятся к тому же контейнер.

7. Большинство алгоритмических шаблонов библиотеки, которые работают на структурах данных есть интерфейсы, которые используют диапазоны. Диапазон - это пара итераторов, которые обозначают начало и конец вычисления. Диапазон [i, i) - пустой диапазон; в общем случае диапазон [i, j) относится к элементы в структуре данных, начиная с той, на которую указывает i и до, но не включая тот, на который указывает j. Диапазон [i, j) равен действителен тогда и только тогда, когда j доступен из i. Результат применение функций в библиотеке к недопустимым диапазонам undefined.

Ответ 2

Результат Undefined.


С++ 03 Стандарт: 25.1.1 Для каждого и
С++ 11 Стандарт: 25.2.4 Для каждого состояния:

template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);

1 Эффекты: Применяет f к результату разыменования каждого итератора в диапазоне [первый, последний], начиная от первого и до последнего - 1

В то время как другой раздел определяет допустимый диапазон [first,last) как:

С++ 03 Стандарт: 24.1 Требования к итератору и
С++ 11 Стандарт: 24.2.1 Требования к итератору

Пара 7 для обоих:

Большинство алгоритмических шаблонов библиотеки, которые работают с структурами данных, имеют интерфейсы, которые используют диапазоны. Диапазон - это пара итераторов, которые обозначают начало и конец вычисления. Диапазон [i, i) - пустой диапазон; в общем случае диапазон [i, j) относится к элементам в структуре данных, начиная с того, на который указывает i, и до, но не включая тот, на который указывает j. Диапазон [i, j) действителен тогда и только тогда, когда j доступен из i. Результатом применения функций в библиотеке к недопустимым диапазонам является Undefined.


Вспомнив прочтение этого места, просто просмотрел:

Стандартная библиотека С++ - Учебное пособие и справочник - Николас Йосутильс

Это находит упоминание в:

5.4.1 Диапазоны
Вызывающий должен убедиться, что первый и второй аргументы определяют допустимый диапазон. Это тот случай, когда конец диапазона доступен с самого начала, итерации через элементы. Это означает, что программист должен гарантировать, что оба итератора принадлежат одному и тому же контейнеру и что начало не стоит за концом. Если это не так, поведение undefined и бесконечные циклы или доступ к запрещенной памяти могут привести к.

Ответ 3

Означает ли это, что следующее технически обосновано? Или это undefined? Что я могу реально ожидать от этого?

Нет, это не так. Ваш код будет демонстрировать поведение undefined, когда for_each увеличивает итератор и что итератор будет указывать на end, и нет ничего, чтобы разыменовать (ну, этого достаточно, чтобы получить поведение undefined в этой точке, так что нет смысла говорить о прошлом)!

Ответ 4

Это объясняется разделом 24.1 стандарта "Требования к итератору":

Итератор j называется достижимым из итератора i тогда и только тогда, когда существует конечная последовательность приложений выражения ++i, которая делает i == j. Если j доступен из i, они относятся к одному и тому же контейнеру.

...

Диапазон [i, j) действителен тогда и только тогда, когда j доступен из i. Результатом применения функций в библиотеке к недопустимым диапазонам является undefined.

Итак, v.begin() + 3 достижимо от v.begin(), но не наоборот. Таким образом, [v.begin()+3, v.begin()) не является допустимым диапазоном, а ваш вызов for_each равен undefined.

Ответ 5

Стандарт определяет ограничения сложности для функций, принимающих диапазоны. В конкретном случае for_each (25.2.4 в стандарте С++):

Сложность: применяет f ровно last - first раз

Таким образом, это эффективно no-op в вашем примере.