On Mon, 7 Oct 2024, Patrick Palka wrote: > Tested on x86_64-pc-linux-gnu, does this look OK for trunk only? > This doesn't seem worth backporting since there should be no > behavior change. > > -- >8 -- > > 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. > > Note the main difference between the two is that decay-copy materializes > its argument whereas auto(x) doesn't, 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).
I should note this patch is treating this paper as a DR against C++20, which should be fine since there should be no behavior change in practice (especially in light of LWG 3724 which constrains decay-copy to make it SFINAE-friendly). > > After this paper the only remaining uses of decay-copy in the library > 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 mirror the wording and use __decay_copy explicitly. So > since it's apparently no longer needed this patch goes ahead and removes > __decay_copy. > > libstdc++-v3/ChangeLog: > > * 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. > --- > 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 29d795f687c..fdbf90e28fc 100644 > --- a/libstdc++-v3/include/bits/c++config > +++ b/libstdc++-v3/include/bits/c++config > @@ -265,6 +265,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 490a362cdf1..0fcfed56737 100644 > --- a/libstdc++-v3/include/bits/iterator_concepts.h > +++ b/libstdc++-v3/include/bits/iterator_concepts.h > @@ -1003,19 +1003,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. > @@ -1025,7 +1016,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 cb2eba1f841..80ff1e300ce 100644 > --- a/libstdc++-v3/include/bits/ranges_base.h > +++ b/libstdc++-v3/include/bits/ranges_base.h > @@ -115,9 +115,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: > @@ -142,7 +142,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. > @@ -152,7 +152,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 > @@ -165,9 +165,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: > @@ -192,7 +192,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; > @@ -201,7 +201,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> > @@ -219,9 +219,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&>()))) > @@ -254,7 +254,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)))>; > }; > > @@ -264,7 +264,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)))>; > }; > > @@ -276,9 +276,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&>()))) > @@ -312,7 +312,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; > @@ -322,7 +322,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> > @@ -347,9 +347,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&>())); > @@ -459,7 +459,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> > @@ -473,7 +473,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&>())); > } > -- > 2.47.0.rc1.33.g90fe3800b9 > >