Как я мог сделать свой ленивый итератор?

Я создаю класс С++ 11, который создает огромное количество данных. Эти данные в настоящее время поступают из базы данных и не могут полностью помещаться в память. Я хотел бы предоставить пользователю итератор, который ведет себя как обычные STL-итераторы, но это было бы лениво. Точнее, я мог бы сделать что-то подобное:

for (auto& item : big_bunch_of_data) {
    do_stuff_with(item);
}

Если элемент извлекается из базы данных только на каждой итерации. Если я прав, этот новый синтаксис - сахар для

for (stuff::iterator it = big_bunch_of_data.begin();it != big_bunch_of_data.end();it++) {
    do_stuff_with(*it);
}

Означает ли это, что, предоставляя begin, end и operator++, я мог бы иметь желаемое поведение? И что должны делать эти методы? Я имею в виду, могу ли я сделать их ленивыми, не разбирая вещи?

Ответ 1

Почти; компилятор будет искать в нескольких других местах начальный и конечный итераторы, если он не сможет найти методы begin или end в классе контейнера; так работают циклы на основе диапазона для массивов, которые не имеют членов begin и end. Он также будет искать бесплатные функции begin и end от ADL, и, в конечном итоге, std::begin и std::end, так что есть много возможностей для модификации на основе диапазона для поддержки петель для существующих контейнеров. Раздел 6.5.4 содержит подробности.

На ваш другой вопрос, итераторы абсолютно могут быть ленивыми! Хорошим примером является std::istream_iterator, который должен быть ленивым, поскольку он читает ввод с консоли.

Требование использовать итератор в цикле for состоит в том, что он должен удовлетворять категории входных итераторов, что описано в разделе 24.2.3; для этой категории необходимы следующие операции: !=, унарный *, pre- и постинкрементное ++.

Чтобы сообщить языку, что вы создали входной итератор, вы должны наследовать от std::iterator<std::input_iterator_tag, T, void, T *, T &>, где T - это тип, с которым работает ваш итератор (раздел 24.4.3).