https://gcc.gnu.org/g:a4e8d1884e0e9ed43add45d71278e618a729ff88
commit r16-120-ga4e8d1884e0e9ed43add45d71278e618a729ff88 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Dec 12 00:32:08 2024 +0000 libstdc++: Add lvalue overload for generator::yield_value 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. Reviewed-by: Tomasz Kamiński <tkami...@redhat.com> Reviewed-by: Arsen Arsenović <ar...@aarsen.me> Diff: --- libstdc++-v3/include/std/generator | 10 ++++ .../24_iterators/range_generators/lwg3899.cc | 57 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/libstdc++-v3/include/std/generator b/libstdc++-v3/include/std/generator index 3f781f1bb29f..7ab2c9e7ce9d 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 000000000000..5a812ecf9d97 --- /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]); +}