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 > >