https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119469
--- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> --- The bug is because I implemented the result of ranges::iter_move in terms of remove_reference_t<decltype(*E)>&& because that's what I assume the type of std::move(*E) to be. But when E is a function reference, *E first decays it to a pointer, then dereferences the pointer, and gives you back the original function reference. What I don't understand is why the type of std::move(*E) is not the return type of std::move(*E)! template<typename _Tp, typename _Up = _Tp&&> _Up __declval(int); template<typename _Tp> _Tp __declval(long); template<typename _Tp> auto declval() noexcept -> decltype(__declval<_Tp>(0)); template<typename T> struct remove_reference { using type = T; }; template<typename T> struct remove_reference<T&> { using type = T; }; template<typename T> struct remove_reference<T&&> { using type = T; }; template<typename T> using remove_reference_t = typename remove_reference<T>::type; template<typename T> remove_reference_t<T>&& move(T&& t) { return static_cast<remove_reference_t<T>&&>(t); } template<typename, typename> constexpr bool is_same_v = false; template<typename T> constexpr bool is_same_v<T,T> = true; template<typename T, typename U> concept same_as = is_same_v<T,U>; using F = int(); static_assert( same_as<decltype(*declval<F&>()), F&> ); // This fails: // static_assert( same_as<decltype(move(*declval<F&>())), remove_reference_t<F&>&&> ); // Because: static_assert( same_as<decltype(move(*declval<F&>())), F&> ); static_assert( same_as<remove_reference_t<F&>&&, F&&> ); I don't understand why the return type of move<F&> is not F&&