Когда использовать сопрограммы над итераторами?
Допустим, у меня есть данные, которые я хочу предоставить "по одному". Там много данных, поэтому я получаю один кусок только тогда, когда он нужен (для экономии памяти). Так что я не могу хранить все данные внутри std::vector
,
Сегодня я могу использовать итераторы для этого, так как они естественно соответствуют этому требованию. Но я также мог бы использовать сопрограммы (в настоящее время использую Coroutines TS). Использование алгоритмов, использующих только итераторы, не требуется.
Есть ли в этом случае преимущество использования сопрограмм перед итераторами?
1 ответ
Итераторы действуют как своего рода клей, позволяющий пользователям писать алгоритмы, которые работают с последовательностями значений, не зная о том, как эта последовательность появилась или удерживается. Но конкретный "клей" между алгоритмом и последовательностью не имеет значения. Это имеет значение только в том смысле, что конкретная реализация алгоритма должна быть реализована в терминах определенного вида "клея".
Модель итератора стандартной библиотеки полезна, потому что стандартная библиотека поставляется с инструментами, которые используют эту модель (алгоритмы, конструкторы итераторов контейнеров, основанные на диапазоне for
, так далее). Если вы на самом деле не используете эти механизмы... тогда нет ничего объективного в использовании итерационной модели по сравнению с любой другой моделью. Вы могли бы просто иметь объект, который имеет get_next
функция и has_next
или какой-то похожий интерфейс. Все они примерно одинаково эффективны, и нетрудно перейти от одного к другому.
Сопрограммы были бы полезны в этом отношении только в той степени, в которой это упрощает выполнение операции. Код, использующий сопрограмму генерации, будет иметь в основном тот же интерфейс, что и раньше; это просто внутренне использует co_yield
и кадр стека, который останавливается и возобновляется.
Поскольку стековый фрейм сопрограммы - это объект, который сохраняется, вам не нужно явно создавать объект генерации. Функция, которая генерирует значения, может использовать переменные стека для своего состояния, затем co_yield
значения из этого стека данных по мере необходимости. Это позволило бы вам создать обобщенную структуру генератора, которую могли бы использовать многие различные функции, таким образом создавая некоторое разделение между общим интерфейсом для всех генераторов и конкретным кодом, выполняющим генерацию.