The result of std::move (or a cast to an rvalue reference) on a function
reference is always an lvalue. Because std::ranges::iter_move was using
the type std::remove_reference_t<X>&& as the result of std::move, it was
giving the wrong type for function references. Use a decltype-specifier
with declval<remove_reference_t<X>>() instead of just using the
remove_reference_t<X>&& type directly. This gives the right result,
while still avoiding the cost of doing overload resolution for
std::move.

libstdc++-v3/ChangeLog:

        PR libstdc++/119469
        * include/bits/iterator_concepts.h (_IterMove::__result): Use
        decltype-specifier instead of an explicit type.
        * testsuite/24_iterators/customization_points/iter_move.cc:
        Check results for function references.
---

This one is weird, but I think the fix is right.

Testing x86_64-linux.

 libstdc++-v3/include/bits/iterator_concepts.h         | 11 +++++++++--
 .../24_iterators/customization_points/iter_move.cc    | 11 +++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/iterator_concepts.h 
b/libstdc++-v3/include/bits/iterator_concepts.h
index a201e24d2e2..e36556dc512 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -133,12 +133,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          struct __result<_Tp>
          { using type = decltype(iter_move(std::declval<_Tp>())); };
 
-       // Otherwise, if *E if an lvalue, use std::move(*E).
+       // Otherwise, if *E is an lvalue, use std::move(*E).
        template<typename _Tp>
          requires (!__adl_imove<_Tp>)
            && is_lvalue_reference_v<__iter_ref_t<_Tp>>
          struct __result<_Tp>
-         { using type = remove_reference_t<__iter_ref_t<_Tp>>&&; };
+         {
+           // Instead of decltype(std::move(*E)) we define the type as the
+           // return type of std::move, i.e. remove_reference_t<iter_ref>&&.
+           // N.B. the use of decltype(declval<X>()) instead of just X&& is
+           // needed for function reference types, see PR libstdc++/119469.
+           using type
+             = decltype(std::declval<remove_reference_t<__iter_ref_t<_Tp>>>());
+         };
 
        template<typename _Tp>
          static constexpr bool
diff --git 
a/libstdc++-v3/testsuite/24_iterators/customization_points/iter_move.cc 
b/libstdc++-v3/testsuite/24_iterators/customization_points/iter_move.cc
index 341bd5b98d7..87375746dfd 100644
--- a/libstdc++-v3/testsuite/24_iterators/customization_points/iter_move.cc
+++ b/libstdc++-v3/testsuite/24_iterators/customization_points/iter_move.cc
@@ -157,9 +157,20 @@ test_pr106612()
   static_assert( std::same_as<decltype(std::ranges::iter_move(I3{})), F> );
 }
 
+void
+test_pr119469()
+{
+  // rvalue references to function types are weird.
+  using F = int();
+  static_assert( std::same_as<std::iter_rvalue_reference_t<F>, F&> );
+  static_assert( std::same_as<std::iter_rvalue_reference_t<F&>, F&> );
+  static_assert( std::same_as<std::iter_rvalue_reference_t<F&&>, F&> );
+}
+
 int
 main()
 {
   test01();
   test_adl();
+  test_pr119469();
 }
-- 
2.49.0

Reply via email to