libstdc++: Fix std::ranges::iter_move for function references [PR119469]

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.

Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
Jonathan Wakely 2025-03-26 11:21:32 +00:00 committed by Jonathan Wakely
parent b631ff45f2
commit 3e52eb28c5
No known key found for this signature in database
2 changed files with 20 additions and 2 deletions

View file

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

View file

@ -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();
}