On Thu, Apr 24, 2025 at 2:41 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> This was approved in Wrocław as LWG 3899. > > This avoids creating a new coroutine frame to co_yield the elements of > an lvalue generator. > > libstdc++-v3/ChangeLog: > > * include/std/generator (generator::yield_value): Add overload > taking lvalue element_of view, as per LWG 3899. > * testsuite/24_iterators/range_generators/lwg3899.cc: New test. > --- > > Added a testcase (thanks to Arsen). > > Tested x86_64-linux. > LGTM > > libstdc++-v3/include/std/generator | 10 ++++ > .../24_iterators/range_generators/lwg3899.cc | 57 +++++++++++++++++++ > 2 files changed, 67 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc > > diff --git a/libstdc++-v3/include/std/generator > b/libstdc++-v3/include/std/generator > index 3f781f1bb29..7ab2c9e7ce9 100644 > --- a/libstdc++-v3/include/std/generator > +++ b/libstdc++-v3/include/std/generator > @@ -153,6 +153,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > noexcept > { return _Recursive_awaiter { std::move(__r.range) }; } > > + // _GLIBCXX_RESOLVE_LIB_DEFECTS > + // 3899. co_yielding elements of an lvalue generator is > + // unnecessarily inefficient > + template<typename _R2, typename _V2, typename _A2, typename _U2> > + requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> > + auto > + yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&, _U2> > __r) > + noexcept > + { return _Recursive_awaiter { std::move(__r.range) }; } > + > template<ranges::input_range _R, typename _Alloc> > requires convertible_to<ranges::range_reference_t<_R>, _Yielded> > auto > diff --git > a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc > new file mode 100644 > index 00000000000..5a812ecf9d9 > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc > @@ -0,0 +1,57 @@ > +// { dg-do run { target c++23 } } > + > +// LWG 3899. > +// co_yielding elements of an lvalue generator is unnecessarily > inefficient > + > +#include <generator> > +#include <memory_resource> > +#include <testsuite_hooks.h> > + > +struct memory_resource : std::pmr::memory_resource > +{ > + std::size_t count = 0; > + > + void* do_allocate(std::size_t n, std::size_t a) override > + { > + count += n; > + return std::pmr::new_delete_resource()->allocate(n, a); > + } > + > + void do_deallocate(void* p, std::size_t n, std::size_t a) override > + { > + return std::pmr::new_delete_resource()->deallocate(p, n, a); > + } > + > + bool do_is_equal(const std::pmr::memory_resource& mr) const noexcept > override > + { return this == &mr; } > +}; > + > +std::pmr::generator<int> > +f(std::allocator_arg_t, std::pmr::polymorphic_allocator<>, int init) > +{ > + co_yield init + 0; > + co_yield init + 1; > +} > + > +std::pmr::generator<int> > +g(std::allocator_arg_t, std::pmr::polymorphic_allocator<> alloc) > +{ > + auto gen = f(std::allocator_arg, alloc, 0); > + auto gen2 = f(std::allocator_arg, alloc, 2); > + co_yield std::ranges::elements_of(std::move(gen), alloc); > + co_yield std::ranges::elements_of(gen2, alloc); > +} > + > +int > +main() > +{ > + std::size_t counts[4]; > + memory_resource mr; > + for (auto d : g(std::allocator_arg , &mr)) > + counts[d] = mr.count; > + VERIFY(counts[0] != 0); > + // No allocations after the first one: > + VERIFY(counts[1] == counts[0]); > + VERIFY(counts[2] == counts[0]); > + VERIFY(counts[3] == counts[0]); > +} > -- > 2.49.0 > >