Created on 2025-10-16.00:00:00 last changed 7 days ago
Proposed resolution:
This wording is relative to N5014.
Modify [coro.generator.promise] as indicated:
[Drafting note: The following changes would check that `co_yield *i` would match one of the overloads of `yield_value()` described in paragraphs 4 or 6. Alternatively this could be expressed more directly as a helper comment or in prose in a Mandates: element.
[…]namespace std { template<class Ref, class Val, class Allocator> class generator<Ref, Val, Allocator>::promise_type { public: […] template<ranges::input_range R, class Alloc> requires convertible_to<ranges::range_reference_t<R>, yielded> || (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>) auto yield_value(ranges::elements_of<R, Alloc> r); […] }; }template<ranges::input_range R, class Alloc> requires convertible_to<ranges::range_reference_t<R>, yielded> || (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>) auto yield_value(ranges::elements_of<R, Alloc> r);-13- 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_yieldstatic_cast<yielded>(*i); } }; return yield_value(ranges::elements_of(nested( allocator_arg, r.allocator, ranges::begin(r.range), ranges::end(r.range))));
[ 2025-10-23; Reflector poll. ]
Set priority to 2 after reflector poll.
The current resolution is incorrect, and breaks following example:
std::generator<std::vector<int>> g()
{
  std::vector<int> v = {1, 2, 3};
  co_yield std::ranges::elements_of(v);
} 	
The constraints are equivalent to checking if `yield_value(*it)` is well-formed, and maybe we could express is directly as:
requires requires (promise_type p, ranges::iterator_t<R> it) {
    p.yield_value(*it);
}
The std::generator proposal (P2502) explicitly says that the following example is supposed to work, but it does not compile:
For convenience, we further propose that `co_yield elements_of(x)` be extended to support yielding the values of arbitrary ranges beyond generators, ie
std::generator<int> f() { std::vector<int> v = /*... */; co_yield std::ranges::elements_of(v); }
This doesn't compile because the overload listed in [coro.generator.promise] p13 requires convertible_to<ranges::range_reference_t<R>, yielded> (i.e. convertible_to<int&, int&&>) which isn't satisfied.
So it seems clear enough that something got lost in the translation from the design intent to wording. My understanding of the intent is that `co_yield elements_of(rng)` should be semantically similar to the following code, but require a specific optimization in the case where `rng` is the same type of generator as the current coroutine:
for (auto&& x : rng) {
  co_yield std::forward<decltype(x)>(x);
}
Note that that code does compile correctly where `rng` is a vector<int>, so I think the original code should as well.
Speculation on how this happened: Prior to P2529, the generator<T> in P2502R0 was basically the same as generator<const T&> where co_yield elements_of(vector<int>()) does work. After P2529 (which was a design paper w/o wording) was approved by LEWG, P2502R1 was updated to change the reference type to T&&. However, when making the change, I assume that the impact it had on the `elements_of`-non-generator overload of `yield_value` was not considered, so it was unmodified.| History | |||
|---|---|---|---|
| Date | User | Action | Args | 
| 2025-10-23 11:14:55 | admin | set | messages: + msg15383 | 
| 2025-10-19 10:10:45 | admin | set | messages: + msg15259 | 
| 2025-10-16 00:00:00 | admin | create | |