Правильно ли определено сравнение с итератором, инициализированным значением?

Вызывает ли следующая программа неопределенное поведение?

#include <iostream>
#include <iterator>

int main(int argc, char* argv[])
{
    for (auto it = std::istream_iterator<std::string>(std::cin);
         it != std::istream_iterator<std::string>();
         ++it)
    {
        std::cout << *it << " ";
    }

    return 0;
}

В этом четырехлетнем вопросе говорится, что их нельзя сравнивать:

Итераторы также могут иметь сингулярные значения, не связанные ни с одним контейнером. [Пример: после объявления неинициализированного указателя x (как в случае с int* x;) всегда следует предполагать, что x имеет единственное значение указателя. ] Результаты большинства выражений не определены для сингулярных значений; единственным исключением является присвоение неединственного значения итератору, который содержит единственное значение.

Но другой ответ говорит о стандарте С++ 14:

Однако итераторы, инициализированные значением, могут сравниваться и должны сравниваться с другими итераторами, инициализируемыми значением того же типа.


person user4118242    schedule 07.10.2014    source источник
comment
Не привыкли использовать итераторы таким образом, когда закончится цикл (или вы ожидаете)?   -  person Neil Kirk    schedule 07.10.2014
comment
Этот цикл действительно четко определен, как и любой другой цикл итератора, у вас есть его начало и цикл до конца. Также очень часто используются итераторы входного потока, подобные этому, в вызовах, например. std::copy, чтобы получить все значения из потока, например, в вектор, см. пример кода на этой std::istream_iterator справочной странице.   -  person Some programmer dude    schedule 07.10.2014
comment
Я боюсь, что вы смешиваете итераторы сравнения и сравниваете значения разыменованных итераторов, где разыменование end() не определено.   -  person    schedule 07.10.2014


Ответы (1)


Вы смешиваете две разные проблемы.

istream_iterator - это итератор ввода, а не итератор вперед, поэтому указанное вами изменение С++ 14 к нему вообще не относится. Вам разрешено сравнивать istream_iterator таким образом, потому что они явно указаны для разрешения таких сравнений. Стандарт говорит, что (§24.6.1 [istream.iterator])

Конструктор без аргументов istream_iterator() всегда создает объект итератора ввода конца потока, который является единственным законным итератором, который можно использовать для конечного условия. [...]

Два итератора конца потока всегда равны. Итератор конца потока не равен итератору конца потока. Два итератора, отличные от конца потока, равны, если они созданы из одного и того же потока.

Для прямых итераторов (которые также включают двунаправленные и произвольные) итераторы, инициализированные значением, в целом сопоставляются друг с другом в C++14. Если ваша стандартная библиотека реализует это, вы можете сравнить два итератора, инициализируемых значением. Это позволяет вам создать пустой диапазон без базового контейнера. Однако вам по-прежнему не разрешено сравнивать невырожденный итератор с итератором, инициализированным значением. Следующий код имеет неопределенное поведение даже в C++14:

std::list<int> l;

if(l.begin() == std::list<int>::iterator())
    foo();
else 
    bar();
person T.C.    schedule 07.10.2014