26 Ranges library [ranges]

26.8 Range generators [coro.generator]

26.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 form co_­yield elements_­of(r), each element of the range r is 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 << ' '; // prints 0 1 2 } — end example]

26.8.2 Header <generator> synopsis [generator.syn]

namespace std { // [coro.generator.class], class template generator template<class Ref, class V = void, class Allocator = void> class generator; }

26.8.3 Class template generator [coro.generator.class]

namespace std { template<class Ref, class V = void, class Allocator = void> class generator : public ranges::view_interface<generator<Ref, V, Allocator>> { private: using value = conditional_t<is_void_v<V>, remove_cvref_t<Ref>, V>; // exposition only using reference = conditional_t<is_void_v<V>, Ref&&, Ref>; // exposition only // [coro.generator.iterator], class generator​::​iterator class iterator; // exposition only public: using yielded = conditional_t<is_reference_v<reference>, reference, const reference&>; // [coro.generator.promise], class generator​::​promise_­type 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; // exposition only unique_ptr<stack<coroutine_handle<>>> active_; // exposition only }; }
Mandates:
If Allocator is not void, it shall meet the Cpp17Allocator requirements.
Specializations of generator model view and input_­range.
The behavior of a program that adds a specialization for generator is undefined.

26.8.4 Members [coro.generator.members]

generator(generator&& other) noexcept;
Effects: Initializes coroutine_­ with exchange(other.coroutine_­, {}) and active_­ with exchange(​other.active_­, nullptr).
[Note 1:
Iterators previously obtained from other are not invalidated; they become iterators into *this.
— end note]
~generator();
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_);
Returns: *this.
[Note 3:
Iterators previously obtained from other are not invalidated; they become iterators into *this.
— end note]
iterator begin();
Preconditions: coroutine_­ refers to a coroutine suspended at its initial suspend point ([dcl.fct.def.coroutine]).
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.

26.8.5 Class generator​::​promise_­type [coro.generator.promise]

namespace std { template<class Ref, class V, class Allocator> class generator<Ref, V, 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<T2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<T2, 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) noexcept; 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> requires same_­as<Allocator, void> || convertible_­to<const Alloc&, Allocator> void* operator new(size_t size, allocator_arg_t, const Alloc& alloc, const Args&...); template<class This, class Alloc, class... Args> requires same_­as<Allocator, void> || convertible_­to<const Alloc&, Allocator> 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; // exposition only exception_ptr except_; // exposition only }; }
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 *this is 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 by x.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).
Returns: {}.
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>&>;
Preconditions: A handle referring to the coroutine whose promise object is *this is at the top of *active_­ of some generator object x.
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.
Remarks: A yield-expression that calls this function has type void ([expr.yield]).
template<class T2, class V2, class Alloc2, class Unused> requires same_­as<typename generator<T2, V2, Alloc2>::yielded, yielded> auto yield_value(ranges::elements_of<generator<T2, V2, Alloc2>&&, Unused> g) noexcept;
Preconditions: A handle referring to the coroutine whose promise object is *this is at the top of *active_­ of some generator object x.
The coroutine referred to by g.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_­suspend pushes g.range.coroutine_­ into *x.active_­ and resumes execution of the coroutine referred to by g.range.coroutine_­, and whose member await_­resume evaluates rethrow_­exception(except_­) if bool(except_­) is true.
If bool(except_­) is false, the await_­resume member has no effects.
Remarks: A yield-expression that calls this function has type void ([expr.yield]).
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) noexcept;
Effects: Equivalent to: auto nested = [](allocator_arg_t, Alloc, ranges::iterator_t<R> i, ranges::sentinel_t<R> s) -> generator<yielded, ranges::range_value_t<R>, 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))));
Remarks: A yield-expression that calls this function has type void ([expr.yield]).
void unhandled_exception();
Preconditions: A handle referring to the coroutine whose promise object is *this is at the top of *active_­ of some generator object x.
Effects: If the handle referring to the coroutine whose promise object is *this is the sole element of *x.active_­, equivalent to: throw; Otherwise, assigns current_­exception() to except_­.
void* operator new(size_t size) requires same_­as<Allocator, void> || default_­initializable<Allocator>; template<class Alloc, class... Args> requires same_­as<Allocator, void> || convertible_­to<const Alloc&, Allocator> void* operator new(size_t size, allocator_arg_t, const Alloc& alloc, const Args&...); template<class This, class Alloc, class... Args> requires same_­as<Allocator, void> || convertible_­to<const Alloc&, Allocator> 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.
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 that operator 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 new with a size argument equal to size.
Effects: Deallocates the storage pointed to by pointer using an allocator equivalent to that used to allocate it.

26.8.6 Class generator​::​iterator [coro.generator.iterator]

namespace std { template<class Ref, class V, class Allocator> class generator<Ref, V, 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==(iterator i, default_sentinel_t); private: coroutine_handle<promise_type> coroutine_; // exposition only }; }
iterator(iterator&& other) noexcept;
Effects: Initializes coroutine_­ with exchange(other.coroutine_­, {}).
iterator& operator=(iterator&& other) noexcept;
Effects: Equivalent to coroutine_­ = exchange(other.coroutine_­, {}).
Returns: *this.
reference operator*() const noexcept(is_nothrow_copy_constructible_v<reference>);
Preconditions: For some generator object x, coroutine_­ is in *x.active_­ and x.active_­->top() refers to a suspended coroutine with promise object p.
Effects: Equivalent to: return static_­cast<reference>(*p.value_­);
iterator& operator++();
Preconditions: For some generator object x, coroutine_­ is in *x.active_­.
Effects: Equivalent to x.active_­->top().resume().
Returns: *this.
void operator++(int);
Effects: Equivalent to ++*this.
friend bool operator==(iterator i, default_sentinel_t);
Effects: Equivalent to: return i.coroutine_­.done();