Date: Tue, 26 Aug 2025 22:51:06 +0800 (GMT+08:00)
Hi,
Recently I find that it's illegal to write std::generator code like:
std::generator<int> Test()
{
std::vector<int> vec{ 0, 1, 2 };
co_yield std::ranges::elements_of(vec);
}
which is very counter-intuitive. This code example is even adopted from the std::generator proposal (P2502R2, page 16), which also thinks it should be legal. In other words, the proposal seems to be inconsistent in intention and wording, so I think there should be a DR to fix it.
Let me briefly analyze what happens first. Remember that here std::generator<int>::yielded == int&&.
So in current specification of std::generator, the standard regulates that for .yield_value() of the promise type (see [coro.generator]):
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))));
So here it's legal, since static_cast<yielded>(*i) (i.e.static_cast<int&&>(int&)) is legal. However, the constraints of .yield_value() reject it:
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);
Here ranges::range_reference_t<R> is int&, and yielded is int&&. That is, int& is explicitly convertible to int&& (which is fine for std::generator), but not implicitly convertible to int&&, making the constraint fail.
So I think there should be a DR to loosen the constraint to make explicit conversion enough.
[This email is also sent to author of P2502]
Liang Jiaming
Recently I find that it's illegal to write std::generator code like:
std::generator<int> Test()
{
std::vector<int> vec{ 0, 1, 2 };
co_yield std::ranges::elements_of(vec);
}
which is very counter-intuitive. This code example is even adopted from the std::generator proposal (P2502R2, page 16), which also thinks it should be legal. In other words, the proposal seems to be inconsistent in intention and wording, so I think there should be a DR to fix it.
Let me briefly analyze what happens first. Remember that here std::generator<int>::yielded == int&&.
So in current specification of std::generator, the standard regulates that for .yield_value() of the promise type (see [coro.generator]):
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))));
So here it's legal, since static_cast<yielded>(*i) (i.e.static_cast<int&&>(int&)) is legal. However, the constraints of .yield_value() reject it:
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);
Here ranges::range_reference_t<R> is int&, and yielded is int&&. That is, int& is explicitly convertible to int&& (which is fine for std::generator), but not implicitly convertible to int&&, making the constraint fail.
So I think there should be a DR to loosen the constraint to make explicit conversion enough.
[This email is also sent to author of P2502]
Liang Jiaming
Received on 2025-08-26 14:51:18