On Wed, Mar 26, 2025 at 12:29 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> 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.
>
Interesting, for T being a function type, T&& is forming rvalue reference
to function type,
but we only yield values of function type, so decltype would give T&.

LGTM.
I am not sold on why I would care about the result of calling iter_move on
function reference
(not iterator returning function reference), but standard seem to require
that we support it,
because * works on them due decay to pointer.


> 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