MiddleКодЧастоЕщё не отвечали
Как реализовать минимальный генератор без std::generator?
Реализуйте тип ленивого целочисленного генератора-корутины без std::generator, чтобы корутину с co_yield можно было итерировать значение за значением.
Ограничения:
- Тело должно выполняться лениво — до первого
next()ничего не происходит. - Каждый
co_yieldдолжен действительно приостанавливать корутину. - Coroutine frame должен уничтожаться ровно один раз (без утечки и double-free).
final_suspend()обязан бытьnoexcept.
#include <coroutine>
struct Generator {
struct promise_type {
// ваш код здесь
};
std::coroutine_handle<promise_type> h;
bool next(); // возобновить, вернуть, есть ли ещё значения
int value() const; // текущее значение
~Generator();
};
Допишите реализацию.
Объявите тип с вложенным promise_type, чей yield_value сохраняет значение и возвращает suspend_always. Оберните coroutine_handle, дайте next()/value() и вызовите destroy() фрейма в деструкторе.
- ✗Вернуть
suspend_neverизyield_value, из-за чего корутина не приостанавливается наco_yield - ✗Забыть
destroy()в деструкторе, что приводит к утечке heap-фрейма - ✗Опустить
noexceptуfinal_suspend(), делая программу некорректной
- →Как дать генератору настоящие итераторы для range-based
for? - →Что добавляет
std::generatorповерх этой ручной версии в C++23?
Минимальный генератор
promise_type определяет поведение; yield_value сохраняет элемент и приостанавливает корутину.
#include <coroutine>
struct Generator {
struct promise_type {
int current;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(int v) { current = v; return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> h;
bool next() { h.resume(); return !h.done(); }
int value() const { return h.promise().current; }
~Generator() { if (h) h.destroy(); }
};
Generator counter() {
for (int i = 0; ; ++i)
co_yield i; // suspend_always: parks the frame, returns to caller
}
initial_suspend is suspend_always, so the body runs lazily — nothing happens until the first next(). yield_value returning suspend_always is what makes each co_yield actually pause. The destructor destroy()s the frame; coroutine_handle does not own it.