https://gcc.gnu.org/g:b8314ebff2495ee22f9e2203093bdada9843a0f5
commit r15-6247-gb8314ebff2495ee22f9e2203093bdada9843a0f5 Author: Patrick Palka <ppa...@redhat.com> Date: Fri Dec 13 13:17:29 2024 -0500 libstdc++: Avoid unnecessary copies in ranges::min/max [PR112349] Use a local reference for the (now possibly lifetime extended) result of *__first so that we copy it only when necessary. PR libstdc++/112349 libstdc++-v3/ChangeLog: * include/bits/ranges_algo.h (__min_fn::operator()): Turn local object __tmp into a reference. * include/bits/ranges_util.h (__max_fn::operator()): Likewise. * testsuite/25_algorithms/max/constrained.cc (test04): New test. * testsuite/25_algorithms/min/constrained.cc (test04): New test. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Diff: --- libstdc++-v3/include/bits/ranges_algo.h | 4 ++-- libstdc++-v3/include/bits/ranges_util.h | 4 ++-- .../testsuite/25_algorithms/max/constrained.cc | 25 ++++++++++++++++++++++ .../testsuite/25_algorithms/min/constrained.cc | 25 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index 80d4f5a0d574..772bf4dd997b 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -2952,11 +2952,11 @@ namespace ranges auto __result = *__first; while (++__first != __last) { - auto __tmp = *__first; + auto&& __tmp = *__first; if (std::__invoke(__comp, std::__invoke(__proj, __result), std::__invoke(__proj, __tmp))) - __result = std::move(__tmp); + __result = std::forward<decltype(__tmp)>(__tmp); } return __result; } diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index 7be76e07899b..b7a3c7a03ad3 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -757,11 +757,11 @@ namespace ranges auto __result = *__first; while (++__first != __last) { - auto __tmp = *__first; + auto&& __tmp = *__first; if (std::__invoke(__comp, std::__invoke(__proj, __tmp), std::__invoke(__proj, __result))) - __result = std::move(__tmp); + __result = std::forward<decltype(__tmp)>(__tmp); } return __result; } diff --git a/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc index e7269e1b734a..ad2d47f2f101 100644 --- a/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc @@ -73,10 +73,35 @@ test03() VERIFY( ranges::max({2,3,1,4}, ranges::greater{}, std::negate<>{}) == 4 ); } +void +test04() +{ + // PR libstdc++/112349 - ranges::max/min make unnecessary copies + static int copies, moves; + struct A { + A(int m) : m(m) { } + A(const A& other) : m(other.m) { ++copies; } + A(A&& other) : m(other.m) { ++moves; } + A& operator=(const A& other) { m = other.m; ++copies; return *this; } + A& operator=(A&& other) { m = other.m; ++moves; return *this; } + int m; + }; + A r[5] = {5, 4, 3, 2, 1}; + ranges::max(r, ranges::less{}, &A::m); + VERIFY( copies == 1 ); + VERIFY( moves == 0 ); + copies = moves = 0; + A s[5] = {1, 2, 3, 4, 5}; + ranges::max(s, ranges::less{}, &A::m); + VERIFY( copies == 5 ); + VERIFY( moves == 0 ); +} + int main() { test01(); test02(); test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc index 7198df69adf7..17048fda6394 100644 --- a/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc @@ -73,10 +73,35 @@ test03() VERIFY( ranges::min({2,3,1,4}, ranges::greater{}, std::negate<>{}) == 1 ); } +void +test04() +{ + // PR libstdc++/112349 - ranges::max/min make unnecessary copies + static int copies, moves; + struct A { + A(int m) : m(m) { } + A(const A& other) : m(other.m) { ++copies; } + A(A&& other) : m(other.m) { ++moves; } + A& operator=(const A& other) { m = other.m; ++copies; return *this; } + A& operator=(A&& other) { m = other.m; ++moves; return *this; } + int m; + }; + A r[5] = {5, 4, 3, 2, 1}; + ranges::min(r, ranges::less{}, &A::m); + VERIFY( copies == 5 ); + VERIFY( moves == 0 ); + copies = moves = 0; + A s[5] = {1, 2, 3, 4, 5}; + ranges::min(s, ranges::less{}, &A::m); + VERIFY( copies == 1 ); + VERIFY( moves == 0 ); +} + int main() { test01(); test02(); test03(); + test04(); }