On Wed, 5 Mar 2025, Patrick Palka wrote:

> On Thu, 24 Oct 2024, Jonathan Wakely wrote:
> 
> > On Wed, 9 Oct 2024 at 14:02, Patrick Palka <ppa...@redhat.com> wrote:
> > >
> > > 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).
> > 
> > Yes, that seems fine.
> > 
> > OK for trunk, thanks.
> 
> Thanks for the review.  I never pushed this patch because I had a
> last-minute concern about the new macro _GLIBCXX_AUTO_CAST potentially
> introducing mangling changes between C++20 / 23 if it's used directly
> in a function signature (it's not currently, but in the future it
> might), that I never got around to raising.
> 
> I was thinking about "hardening" the macro by defining it in terms of
> the __decay built-in trait if it's available?  That way, if it's
> used in a function signature then any attempts at mangling the function
> will result in an error due to built-in traits not being mangleable.

Alternatively we could unconditionally define it as

  ::std::__decay_t<decltype((X))>(X)

even in C++23 mode, which should always be equivalent to auto(X)?

> 
> Is this something we care about?  If not, shall we push this for GCC 15
> or wait until stage 1?  Seems rather safe, and affects only C++20 code.
> 
> > 
> > 
> > >
> > > >
> > > > 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
> > > >
> > > >
> > >
> > 
> > 
> 

Reply via email to