https://gcc.gnu.org/g:e73a6d982789746d4bce9d0e595203f2a8dbbaa7
commit r16-1116-ge73a6d982789746d4bce9d0e595203f2a8dbbaa7 Author: Patrick Palka <ppa...@redhat.com> Date: Wed Jun 4 14:55:40 2025 -0400 libstdc++: Implement P0849R8 auto(x) library changes This implements the library changes in P0849R8 "auto(x): decay-copy in the language" which consist of replacing most uses of the exposition-only function decay-copy with auto(x) throughout the library wording. We implement this as a DR against C++20 since there should be no behavior change in practice (especially in light of LWG 3724 which makes decay-copy SFINAE-friendly). The main difference between decay-copy and auto(x) is that decay-copy materializes its argument unlike auto(x), and so the latter is a no-op when its argument is a prvalue. Effectively the former could introduce an unnecessary move constructor call in some contexts. In C++20 and earlier we could emulate auto(x) with decay_t<decltype((x))>(x). After this paper the only remaining uses of decay-copy in the standard are in the specification of some range adaptors. In our implementation of those range adaptors I believe decay-copy is already implied which is why we don't use __decay_copy explicitly there. So since it's apparently no longer needed this patch goes ahead and removes __decay_copy. libstdc++-v3/ChangeLog: * include/bits/c++config (_GLIBCXX_AUTO_CAST): Define. * include/bits/iterator_concepts.h (_Decay_copy, __decay_copy): Remove. (__member_begin, __adl_begin): Use _GLIBCXX_AUTO_CAST instead of __decay_copy as per P0849R8. * include/bits/ranges_base.h (_Begin): Likewise. (__member_end, __adl_end, _End): Likewise. (__member_rbegin, __adl_rbegin, _RBegin): Likewise. (__member_rend, __adl_rend, _Rend): Likewise. (__member_size, __adl_size, _Size): Likewise. (_Data): Likewise. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Diff: --- libstdc++-v3/include/bits/c++config | 6 ++++ libstdc++-v3/include/bits/iterator_concepts.h | 13 ++------- libstdc++-v3/include/bits/ranges_base.h | 40 +++++++++++++-------------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config index 676f5eecbbb6..eec3a4a499dd 100644 --- a/libstdc++-v3/include/bits/c++config +++ b/libstdc++-v3/include/bits/c++config @@ -273,6 +273,12 @@ #define _GLIBCXX_NOEXCEPT_QUAL #endif +#if __cpp_auto_cast +# define _GLIBCXX_AUTO_CAST(X) auto(X) +#else +# define _GLIBCXX_AUTO_CAST(X) ::std::__decay_t<decltype((X))>(X) +#endif + // Macro for extern template, ie controlling template linkage via use // of extern keyword on template declaration. As documented in the g++ // manual, it inhibits all implicit instantiations and is used diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 3b73ff9b6b59..d31e4f145107 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -1022,19 +1022,10 @@ namespace ranges { using std::__detail::__class_or_enum; - struct _Decay_copy final - { - template<typename _Tp> - constexpr decay_t<_Tp> - operator()(_Tp&& __t) const - noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>) - { return std::forward<_Tp>(__t); } - } inline constexpr __decay_copy{}; - template<typename _Tp> concept __member_begin = requires(_Tp& __t) { - { __decay_copy(__t.begin()) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(__t.begin()) } -> input_or_output_iterator; }; // Poison pill so that unqualified lookup doesn't find std::begin. @@ -1044,7 +1035,7 @@ namespace ranges concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(begin(__t)) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(begin(__t)) } -> input_or_output_iterator; }; // Simplified version of std::ranges::begin that only supports lvalues, diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index dde164988569..c09f7292067d 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -119,9 +119,9 @@ namespace ranges if constexpr (is_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_begin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().begin())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().begin())); else - return noexcept(__decay_copy(begin(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(begin(std::declval<_Tp&>()))); } public: @@ -146,7 +146,7 @@ namespace ranges template<typename _Tp> concept __member_end = requires(_Tp& __t) { - { __decay_copy(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>; + { _GLIBCXX_AUTO_CAST(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>; }; // Poison pill so that unqualified lookup doesn't find std::end. @@ -156,7 +156,7 @@ namespace ranges concept __adl_end = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>; + { _GLIBCXX_AUTO_CAST(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>; }; struct _End @@ -169,9 +169,9 @@ namespace ranges if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_end<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().end())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().end())); else - return noexcept(__decay_copy(end(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(end(std::declval<_Tp&>()))); } public: @@ -196,7 +196,7 @@ namespace ranges template<typename _Tp> concept __member_rbegin = requires(_Tp& __t) { - { __decay_copy(__t.rbegin()) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator; }; void rbegin() = delete; @@ -205,7 +205,7 @@ namespace ranges concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(rbegin(__t)) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator; }; template<typename _Tp> @@ -223,9 +223,9 @@ namespace ranges _S_noexcept() { if constexpr (__member_rbegin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().rbegin())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rbegin())); else if constexpr (__adl_rbegin<_Tp>) - return noexcept(__decay_copy(rbegin(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(rbegin(std::declval<_Tp&>()))); else { if constexpr (noexcept(_End{}(std::declval<_Tp&>()))) @@ -258,7 +258,7 @@ namespace ranges template<typename _Tp> concept __member_rend = requires(_Tp& __t) { - { __decay_copy(__t.rend()) } + { _GLIBCXX_AUTO_CAST(__t.rend()) } -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>; }; @@ -268,7 +268,7 @@ namespace ranges concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(rend(__t)) } + { _GLIBCXX_AUTO_CAST(rend(__t)) } -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>; }; @@ -280,9 +280,9 @@ namespace ranges _S_noexcept() { if constexpr (__member_rend<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().rend())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rend())); else if constexpr (__adl_rend<_Tp>) - return noexcept(__decay_copy(rend(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(rend(std::declval<_Tp&>()))); else { if constexpr (noexcept(_Begin{}(std::declval<_Tp&>()))) @@ -316,7 +316,7 @@ namespace ranges concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(__t.size()) } -> __detail::__is_integer_like; + { _GLIBCXX_AUTO_CAST(__t.size()) } -> __detail::__is_integer_like; }; void size() = delete; @@ -326,7 +326,7 @@ namespace ranges && !disable_sized_range<remove_cvref_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(size(__t)) } -> __detail::__is_integer_like; + { _GLIBCXX_AUTO_CAST(size(__t)) } -> __detail::__is_integer_like; }; template<typename _Tp> @@ -351,9 +351,9 @@ namespace ranges if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_size<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().size())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().size())); else if constexpr (__adl_size<_Tp>) - return noexcept(__decay_copy(size(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(size(std::declval<_Tp&>()))); else if constexpr (__sentinel_size<_Tp>) return noexcept(_End{}(std::declval<_Tp&>()) - _Begin{}(std::declval<_Tp&>())); @@ -463,7 +463,7 @@ namespace ranges template<typename _Tp> concept __member_data = requires(_Tp& __t) { - { __decay_copy(__t.data()) } -> __pointer_to_object; + { _GLIBCXX_AUTO_CAST(__t.data()) } -> __pointer_to_object; }; template<typename _Tp> @@ -477,7 +477,7 @@ namespace ranges _S_noexcept() { if constexpr (__member_data<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().data())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().data())); else return noexcept(_Begin{}(std::declval<_Tp&>())); }