On Wed, Mar 26, 2025 at 12:29 PM Jonathan Wakely <[email protected]> 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
>
>