Title
co_yield elements_of(vector<int>()) does not compile
Status
new
Section
[coro.generator.promise]
Submitter
Mathias Stearn

Created on 2025-10-16.00:00:00 last changed 7 days ago

Messages

Date: 2025-10-23.11:14:55

Proposed resolution:

This wording is relative to N5014.

  1. 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_yield static_cast<yielded>(*i);
        }
      };
    return yield_value(ranges::elements_of(nested(
      allocator_arg, r.allocator, ranges::begin(r.range), ranges::end(r.range))));
    
Date: 2025-10-15.00:00:00

[ 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);
}
Date: 2025-10-19.17:40:48

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:55adminsetmessages: + msg15383
2025-10-19 10:10:45adminsetmessages: + msg15259
2025-10-16 00:00:00admincreate