[coro.generator] (original) (raw)
25.8.1 Overview [coroutine.generator.overview]
Class template generator presents a view of the elements yielded by the evaluation of a coroutine.
A generator generates a sequence of elements by repeatedly resuming the coroutine from which it was returned.
Elements of the sequence are produced by the coroutine each time a co_yield statement is evaluated.
When the co_yield statement is of the formco_yield elements_of(r), each element of the range ris successively produced as an element of the sequence.
[Example 1: generator<int> ints(int start = 0) { while (true) co_yield start++;} void f() { for (auto i : ints() | views::take(3)) cout << i << ' '; } — _end example_]
25.8.3 Class template generator [coro.generator.class]
namespace std { template<class Ref, class Val = void, class Allocator = void> class generator : public ranges::view_interface<generator<Ref, Val, Allocator>> { private: using value = conditional_t<is_void_v<Val>, remove_cvref_t<Ref>, Val>; using reference = conditional_t<is_void_v<Val>, Ref&&, Ref>; class iterator; public: using yielded = conditional_t<is_reference_v<_reference_>, reference, const reference&>;class promise_type; generator(const generator&) = delete; generator(generator&& other) noexcept;~generator(); generator& operator=(generator other) noexcept;iterator begin(); default_sentinel_t end() const noexcept;private: coroutine_handle<promise_type> coroutine_ = nullptr; unique_ptr<stack<coroutine_handle<>>> active_; };}
Mandates:
- If Allocator is not void,allocator_traits<Allocator>::pointer is a pointer type.
- value is a cv-unqualified object type.
- reference is either a reference type, or a cv-unqualified object type that models copy_constructible.
- Let RRef denote remove_reference_t<_reference_>&&if reference is a reference type, and reference otherwise.
If Allocator is not void, it shall meet the Cpp17Allocator requirements.
The behavior of a program that adds a specialization for generator is undefined.
25.8.4 Members [coro.generator.members]
generator(generator&& other) noexcept;
Effects: Initializes coroutine_ withexchange(other.coroutine_, {}) and_active__ withexchange(other.active_, nullptr).
[Note 1:
Iterators previously obtained from other are not invalidated; they become iterators into *this.
— _end note_]
Effects: Equivalent to:if (coroutine_) { coroutine_.destroy();}
[Note 2:
Ownership of recursively yielded generators is held in awaitable objects in the coroutine frame of the yielding generator, so destroying the root generator effectively destroys the entire stack of yielded generators.
— _end note_]
generator& operator=(generator other) noexcept;
Effects: Equivalent to:swap(coroutine_, other.coroutine_); swap(active_, other.active_);
[Note 3:
Iterators previously obtained from other are not invalidated; they become iterators into *this.
— _end note_]
Effects: Pushes coroutine_ into *active_, then evaluates coroutine_.resume().
Returns: An iterator object whose member _coroutine_refers to the same coroutine as does_coroutine_.
[Note 4:
A program that calls begin more than once on the same generator has undefined behavior.
— _end note_]
default_sentinel_t end() const noexcept;
Returns: default_sentinel.
25.8.5 Class generator::promise_type [coro.generator.promise]
namespace std { template<class Ref, class Val, class Allocator> class generator<Ref, Val, Allocator>::promise_type { public: generator get_return_object() noexcept; suspend_always initial_suspend() const noexcept { return {}; } auto final_suspend() noexcept; suspend_always yield_value(yielded val) noexcept;auto yield_value(const remove_reference_t<yielded>& lval) requires is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>;template<class R2, class V2, class Alloc2, class Unused> requires same_as<typename generator<R2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<R2, V2, Alloc2>&&, Unused> g) noexcept;template<class R2, class V2, class Alloc2, class Unused> requires same_as<typename generator<R2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<R2, V2, Alloc2>&, Unused> g) noexcept;template<ranges::input_range R, class Alloc> requires convertible_to<ranges::range_reference_t<R>, yielded> auto yield_value(ranges::elements_of<R, Alloc> r);void await_transform() = delete;void return_void() const noexcept {} void unhandled_exception();void* operator new(size_t size) requires same_as<Allocator, void> || default_initializable<Allocator>;template<class Alloc, class... Args> void* operator new(size_t size, allocator_arg_t, const Alloc& alloc, const Args&...);template<class This, class Alloc, class... Args> void* operator new(size_t size, const This&, allocator_arg_t, const Alloc& alloc,const Args&...);void operator delete(void* pointer, size_t size) noexcept;private: add_pointer_t<yielded> value_ = nullptr; exception_ptr except_; };}
generator get_return_object() noexcept;
Returns: A generator object whose member _coroutine__is coroutine_handle<promise_type>::from_promise(*this), and whose member active_ points to an empty stack.
auto final_suspend() noexcept;
Preconditions: A handle referring to the coroutine whose promise object is *thisis at the top of *_active__of some generator object x.
This function is called by that coroutine upon reaching its final suspend point ([dcl.fct.def.coroutine]).
Returns: An awaitable object of unspecified type ([expr.await]) whose member functions arrange for the calling coroutine to be suspended, pop the coroutine handle from the top of *x.active_, and resume execution of the coroutine referred to byx._active__->top()if *x.active_ is not empty.
If it is empty, control flow returns to the current coroutine caller or resumer ([dcl.fct.def.coroutine]).
suspend_always yield_value(yielded val) noexcept;
Effects: Equivalent to value_ = addressof(val).
auto yield_value(const remove_reference_t<yielded>& lval) requires is_rvalue_reference_v<yielded> && [constructible_from](concept.constructible#concept:constructible%5Ffrom "18.4.11 Concept constructible_from [concept.constructible]")<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>;
Preconditions: A handle referring to the coroutine whose promise object is *thisis at the top of *_active__of some generator object.
Returns: An awaitable object of an unspecified type ([expr.await]) that stores an object of type remove_cvref_t<yielded>direct-non-list-initialized with lval, whose member functions arrange for_value__ to point to that stored object and then suspend the coroutine.
Throws: Any exception thrown by the initialization of the stored object.
template<class R2, class V2, class Alloc2, class Unused> requires [same_as](concept.same#concept:same%5Fas "18.4.2 Concept same_as [concept.same]")<typename generator<R2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<R2, V2, Alloc2>&&, Unused> g) noexcept;template<class R2, class V2, class Alloc2, class Unused> requires [same_as](concept.same#concept:same%5Fas "18.4.2 Concept same_as [concept.same]")<typename generator<R2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<R2, V2, Alloc2>&, Unused> g) noexcept;
Preconditions: A handle referring to the coroutine whose promise object is *thisis at the top of *_active__of some generator object x.
The coroutine referred to byg.range._coroutine__is suspended at its initial suspend point.
Returns: An awaitable object of an unspecified type ([expr.await]) into which g.range is moved, whose member await_ready returns false, whose member await_suspendpushes g.range._coroutine__into *x._active_and resumes execution of the coroutine referred to by g.range._coroutine_, and whose member await_resume evaluatesrethrow_exception(except_)if bool(except_) is true.
If bool(except_) is false, the await_resume member has no effects.
template<ranges::[input_range](range.refinements#concept:input%5Frange "25.4.5 Other range refinements [range.refinements]") R, class Alloc> requires [convertible_to](concept.convertible#concept:convertible%5Fto "18.4.4 Concept convertible_to [concept.convertible]")<ranges::range_reference_t<R>, yielded> auto yield_value(ranges::elements_of<R, Alloc> r);
Effects: Equivalent to:auto nested = [](allocator_arg_t, Alloc, ranges::iterator_t<R> i, ranges::sentinel_t<R> s) -> generator<yielded, void, Alloc> { for (; i != s; ++i) { co_yield static_cast<yielded>(*i);} };return yield_value(ranges::elements_of(nested( allocator_arg, r.allocator, ranges::begin(r.range), ranges::end(r.range))));
void unhandled_exception();
Preconditions: A handle referring to the coroutine whose promise object is *thisis at the top of *_active__of some generator object x.
Effects: If the handle referring to the coroutine whose promise object is *thisis the sole element of *x.active_, equivalent to throw, otherwise, assigns current_exception() to except_.
void* operator new(size_t size) requires [same_as](concept.same#concept:same%5Fas "18.4.2 Concept same_as [concept.same]")<Allocator, void> || [default_initializable](concept.default.init#concept:default%5Finitializable "18.4.12 Concept default_initializable [concept.default.init]")<Allocator>;template<class Alloc, class... Args> void* operator new(size_t size, allocator_arg_t, const Alloc& alloc, const Args&...);template<class This, class Alloc, class... Args> void* operator new(size_t size, const This&, allocator_arg_t, const Alloc& alloc,const Args&...);
Let A be
- Allocator, if it is not void,
- Alloc for the overloads with a template parameter Alloc, or
- allocator<void> otherwise.
Let B be allocator_traits<A>::template rebind_alloc<U>where U is an unspecified type whose size and alignment are both __STDCPP_DEFAULT_NEW_ALIGNMENT__.
Mandates: allocator_traits<B>::pointer is a pointer type.
For the overloads with a template parameter Alloc,same_as<Allocator, void> || convertible_to<const Alloc&, Allocator>is modeled.
Effects: Initializes an allocator b of type B with A(alloc), for the overloads with a function parameter alloc, and with A() otherwise.
Uses b to allocate storage for the smallest array of U sufficient to provide storage for a coroutine state of size size, and unspecified additional state necessary to ensure thatoperator delete can later deallocate this memory block with an allocator equal to b.
Returns: A pointer to the allocated storage.
void operator delete(void* pointer, size_t size) noexcept;
Preconditions: pointer was returned from an invocation of one of the above overloads of operator newwith a size argument equal to size.
Effects: Deallocates the storage pointed to by pointerusing an allocator equivalent to that used to allocate it.
25.8.6 Class generator::iterator [coro.generator.iterator]
namespace std { template<class Ref, class Val, class Allocator> class generator<Ref, Val, Allocator>::iterator { public: using value_type = value;using difference_type = ptrdiff_t;iterator(iterator&& other) noexcept;iterator& operator=(iterator&& other) noexcept;reference operator*() const noexcept(is_nothrow_copy_constructible_v<_reference_>);iterator& operator++();void operator++(int);friend bool operator==(const iterator& i, default_sentinel_t);private: coroutine_handle<promise_type> coroutine_; };}
_iterator_(_iterator_&& other) noexcept;
Effects: Initializes _coroutine__with exchange(other.coroutine_, {}).
_iterator_& operator=(_iterator_&& other) noexcept;
Effects: Equivalent to_coroutine__ = exchange(other.coroutine_, {}).
_reference_ operator*() const noexcept(is_nothrow_copy_constructible_v<_reference_>);
Preconditions: For some generator object x,coroutine_ is in *x.active_ andx._active__->top() refers to a suspended coroutine with promise object p.
Effects: Equivalent to:return static_cast<_reference_>(*p.value_);
Preconditions: For some generator object x,coroutine_ is in *x.active_.
Effects: Equivalent to x._active__->top().resume().
Effects: Equivalent to ++*this.
friend bool operator==(const _iterator_& i, default_sentinel_t);
Effects: Equivalent to: return i.coroutine_.done();