Message ID | 20240802205054.545397-1-jwakely@redhat.com |
---|---|
State | New |
Headers | show |
Series | libstdc++: Do not use deduced return type for std::forward_like | expand |
On Fri, 2 Aug 2024, Jonathan Wakely wrote: > This isn't properly tested so I'm not pushing it, but I'm sharing it now > for comment. > > -- >8 -- > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > replaces the deduced return type of std::forward_like with a type trait > to compute that type. > > libstdc++-v3/ChangeLog: > > * include/bits/move.h (__forward_like_impl): New metafunction to > compute the return type of std::forward_like. > (forward_like, __like_t): Use it. > * testsuite/20_util/forward_like/2_neg.cc: Adjust expected > errors. > --- > libstdc++-v3/include/bits/move.h | 45 +++++++++---------- > .../testsuite/20_util/forward_like/2_neg.cc | 2 +- > 2 files changed, 23 insertions(+), 24 deletions(-) > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > index bb200c95964..15b7cd07fce 100644 > --- a/libstdc++-v3/include/bits/move.h > +++ b/libstdc++-v3/include/bits/move.h > @@ -88,31 +88,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > #if __glibcxx_forward_like // C++ >= 23 > template<typename _Tp, typename _Up> > - [[nodiscard]] > - constexpr decltype(auto) > - forward_like(_Up&& __x) noexcept > - { > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > - > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > - { > - using _Up2 = remove_reference_t<_Up>; > - if constexpr (__as_rval) > - return static_cast<const _Up2&&>(__x); > - else > - return static_cast<const _Up2&>(__x); > - } > - else > - { > - if constexpr (__as_rval) > - return static_cast<remove_reference_t<_Up>&&>(__x); > - else > - return static_cast<_Up&>(__x); > - } > - } > + struct __forward_like_impl > + { > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_reference) > + template<typename _Xp> using __remove_ref_t = __remove_reference(_Xp); > +#else > + template<typename _Xp> using __remove_ref_t = remove_reference_t<_Xp>; > +#endif > + template<typename _Xp, typename _Yp> > + using _Copy_const = __conditional_t<is_const_v<_Xp>, const _Yp, _Yp>; > + template<typename _Xp, typename _Yp> > + using _Override_ref = __conditional_t<is_rvalue_reference_v<_Xp>, > + __remove_ref_t<_Yp>&&, _Yp&>; Moving these nested alias templates out to namespace scope improves compile time and memory usage of forward_like by about 25% (since nested templates are more expensive to instantiate). However, the following implementation using a single partially specialized class template is a further 20% faster and uses around 15% less memory: -- >8 -- Subject: [PATCH] libstdc++: use concrete return type for std::forward_like Inspired by https://github.com/llvm/llvm-project/issues/101614 this inverts the relationship between forward_like and __like_t, so that forward_like is defined in terms of __like_t and with a concrete return type. __like_t in turn is defined via partial specializations that performs case analysis on the const- and reference-ness of T. This turns out to be more SFINAE friendly and significantly cheaper to compile than the previous implementation. libstdc++-v3/ChangeLog: * include/bits/move.h (__like_impl): New metafunction. (__like_t): Redefine in terms of __like_impl. (forward_like): Redefine in terms of __like_t. * testsuite/20_util/forward_like/2_neg.cc: Don't expect error outside the immediate context anymore. --- libstdc++-v3/include/bits/move.h | 46 +++++++++---------- .../testsuite/20_util/forward_like/2_neg.cc | 6 +-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h index bb200c95964..eaf4280d7ec 100644 --- a/libstdc++-v3/include/bits/move.h +++ b/libstdc++-v3/include/bits/move.h @@ -88,31 +88,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __glibcxx_forward_like // C++ >= 23 template<typename _Tp, typename _Up> - [[nodiscard]] - constexpr decltype(auto) - forward_like(_Up&& __x) noexcept - { - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; - - if constexpr (is_const_v<remove_reference_t<_Tp>>) - { - using _Up2 = remove_reference_t<_Up>; - if constexpr (__as_rval) - return static_cast<const _Up2&&>(__x); - else - return static_cast<const _Up2&>(__x); - } - else - { - if constexpr (__as_rval) - return static_cast<remove_reference_t<_Up>&&>(__x); - else - return static_cast<_Up&>(__x); - } - } + struct __like_impl; // _Tp must be a reference and _Up an lvalue reference + + template<typename _Tp, typename _Up> + struct __like_impl<_Tp&, _Up&> + { using type = _Up&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<const _Tp&, _Up&> + { using type = const _Up&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<_Tp&&, _Up&> + { using type = _Up&&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<const _Tp&&, _Up&> + { using type = const _Up&&; }; template<typename _Tp, typename _Up> - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); + using __like_t = __like_impl<_Tp&&, _Up&>; + + template<typename _Tp, typename _Up> + constexpr __like_t<_Tp, _Up> + forward_like(_Up&& __x) noexcept + { return static_cast<__like_t<_Tp, _Up>>(__x); } #endif /** diff --git a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc index ff835af1915..5dafa419a7e 100644 --- a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc +++ b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc @@ -2,9 +2,7 @@ #include <utility> -auto x1 = std::forward_like<void>(1); // { dg-error "here" } +auto x1 = std::forward_like<void>(1); // { dg-error "no match" } // { dg-error "forming reference to void" "" { target *-*-* } 0 } -auto x2 = std::forward_like<void()const>(1); // { dg-error "here" } +auto x2 = std::forward_like<void()const>(1); // { dg-error "no match" } // { dg-error "forming reference to qualified function" "" { target *-*-* } 0 } - -// { dg-prune-output "inconsistent deduction for auto return type" } // PR111484
On Fri, 2 Aug 2024, Patrick Palka wrote: > On Fri, 2 Aug 2024, Jonathan Wakely wrote: > > > This isn't properly tested so I'm not pushing it, but I'm sharing it now > > for comment. > > > > -- >8 -- > > > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > > replaces the deduced return type of std::forward_like with a type trait > > to compute that type. > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/move.h (__forward_like_impl): New metafunction to > > compute the return type of std::forward_like. > > (forward_like, __like_t): Use it. > > * testsuite/20_util/forward_like/2_neg.cc: Adjust expected > > errors. > > --- > > libstdc++-v3/include/bits/move.h | 45 +++++++++---------- > > .../testsuite/20_util/forward_like/2_neg.cc | 2 +- > > 2 files changed, 23 insertions(+), 24 deletions(-) > > > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > > index bb200c95964..15b7cd07fce 100644 > > --- a/libstdc++-v3/include/bits/move.h > > +++ b/libstdc++-v3/include/bits/move.h > > @@ -88,31 +88,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > #if __glibcxx_forward_like // C++ >= 23 > > template<typename _Tp, typename _Up> > > - [[nodiscard]] > > - constexpr decltype(auto) > > - forward_like(_Up&& __x) noexcept > > - { > > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > > - > > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > > - { > > - using _Up2 = remove_reference_t<_Up>; > > - if constexpr (__as_rval) > > - return static_cast<const _Up2&&>(__x); > > - else > > - return static_cast<const _Up2&>(__x); > > - } > > - else > > - { > > - if constexpr (__as_rval) > > - return static_cast<remove_reference_t<_Up>&&>(__x); > > - else > > - return static_cast<_Up&>(__x); > > - } > > - } > > + struct __forward_like_impl > > + { > > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_reference) > > + template<typename _Xp> using __remove_ref_t = __remove_reference(_Xp); > > +#else > > + template<typename _Xp> using __remove_ref_t = remove_reference_t<_Xp>; > > +#endif > > + template<typename _Xp, typename _Yp> > > + using _Copy_const = __conditional_t<is_const_v<_Xp>, const _Yp, _Yp>; > > + template<typename _Xp, typename _Yp> > > + using _Override_ref = __conditional_t<is_rvalue_reference_v<_Xp>, > > + __remove_ref_t<_Yp>&&, _Yp&>; > > Moving these nested alias templates out to namespace scope improves > compile time and memory usage of forward_like by about 25% (since nested > templates are more expensive to instantiate). > > However, the following implementation using a single partially > specialized class template is a further 20% faster and uses around > 15% less memory: > > -- >8 -- > > Subject: [PATCH] libstdc++: use concrete return type for std::forward_like > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > inverts the relationship between forward_like and __like_t, so that > forward_like is defined in terms of __like_t and with a concrete return > type. __like_t in turn is defined via partial specializations that > performs case analysis on the const- and reference-ness of T. > > This turns out to be more SFINAE friendly and significantly cheaper > to compile than the previous implementation. > > libstdc++-v3/ChangeLog: > > * include/bits/move.h (__like_impl): New metafunction. > (__like_t): Redefine in terms of __like_impl. > (forward_like): Redefine in terms of __like_t. > * testsuite/20_util/forward_like/2_neg.cc: Don't expect > error outside the immediate context anymore. > --- > libstdc++-v3/include/bits/move.h | 46 +++++++++---------- > .../testsuite/20_util/forward_like/2_neg.cc | 6 +-- > 2 files changed, 25 insertions(+), 27 deletions(-) > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > index bb200c95964..eaf4280d7ec 100644 > --- a/libstdc++-v3/include/bits/move.h > +++ b/libstdc++-v3/include/bits/move.h > @@ -88,31 +88,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > #if __glibcxx_forward_like // C++ >= 23 > template<typename _Tp, typename _Up> > - [[nodiscard]] > - constexpr decltype(auto) > - forward_like(_Up&& __x) noexcept > - { > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > - > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > - { > - using _Up2 = remove_reference_t<_Up>; > - if constexpr (__as_rval) > - return static_cast<const _Up2&&>(__x); > - else > - return static_cast<const _Up2&>(__x); > - } > - else > - { > - if constexpr (__as_rval) > - return static_cast<remove_reference_t<_Up>&&>(__x); > - else > - return static_cast<_Up&>(__x); > - } > - } > + struct __like_impl; // _Tp must be a reference and _Up an lvalue reference > + > + template<typename _Tp, typename _Up> > + struct __like_impl<_Tp&, _Up&> > + { using type = _Up&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<const _Tp&, _Up&> > + { using type = const _Up&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<_Tp&&, _Up&> > + { using type = _Up&&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<const _Tp&&, _Up&> > + { using type = const _Up&&; }; > > template<typename _Tp, typename _Up> > - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); > + using __like_t = __like_impl<_Tp&&, _Up&>; Oops, this patch I sent has a bug, this should say typename __like_impl<_Tp&&, _Up&>::type. (The patch I actually benchmarked didn't have this bug, phew :)) Lightly tested, full testing in progress. -- >8 -- Subject: [PATCH] libstdc++: use concrete return type for std::forward_like Inspired by https://github.com/llvm/llvm-project/issues/101614 this inverts the relationship between forward_like and __like_t, so that forward_like is defined in terms of __like_t and with a concrete return type. __like_t in turn is defined via partial specializations that performs case analysis on the const- and reference-ness of T. This turns out to be more SFINAE friendly and significantly cheaper to compile than the previous implementation. libstdc++-v3/ChangeLog: * include/bits/move.h (__like_impl): New metafunction. (__like_t): Redefine in terms of __like_impl. (forward_like): Redefine in terms of __like_t. * testsuite/20_util/forward_like/2_neg.cc: Don't expect error outside the immediate context anymore. --- libstdc++-v3/include/bits/move.h | 46 +++++++++---------- .../testsuite/20_util/forward_like/2_neg.cc | 6 +-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h index bb200c95964..caab3992e4a 100644 --- a/libstdc++-v3/include/bits/move.h +++ b/libstdc++-v3/include/bits/move.h @@ -88,31 +88,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __glibcxx_forward_like // C++ >= 23 template<typename _Tp, typename _Up> - [[nodiscard]] - constexpr decltype(auto) - forward_like(_Up&& __x) noexcept - { - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; - - if constexpr (is_const_v<remove_reference_t<_Tp>>) - { - using _Up2 = remove_reference_t<_Up>; - if constexpr (__as_rval) - return static_cast<const _Up2&&>(__x); - else - return static_cast<const _Up2&>(__x); - } - else - { - if constexpr (__as_rval) - return static_cast<remove_reference_t<_Up>&&>(__x); - else - return static_cast<_Up&>(__x); - } - } + struct __like_impl; // _Tp must be a reference and _Up an lvalue reference + + template<typename _Tp, typename _Up> + struct __like_impl<_Tp&, _Up&> + { using type = _Up&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<const _Tp&, _Up&> + { using type = const _Up&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<_Tp&&, _Up&> + { using type = _Up&&; }; + + template<typename _Tp, typename _Up> + struct __like_impl<const _Tp&&, _Up&> + { using type = const _Up&&; }; template<typename _Tp, typename _Up> - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); + using __like_t = typename __like_impl<_Tp&&, _Up&>::type; + + template<typename _Tp, typename _Up> + constexpr __like_t<_Tp, _Up> + forward_like(_Up&& __x) noexcept + { return static_cast<__like_t<_Tp, _Up>>(__x); } #endif /** diff --git a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc index ff835af1915..5dafa419a7e 100644 --- a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc +++ b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc @@ -2,9 +2,7 @@ #include <utility> -auto x1 = std::forward_like<void>(1); // { dg-error "here" } +auto x1 = std::forward_like<void>(1); // { dg-error "no match" } // { dg-error "forming reference to void" "" { target *-*-* } 0 } -auto x2 = std::forward_like<void()const>(1); // { dg-error "here" } +auto x2 = std::forward_like<void()const>(1); // { dg-error "no match" } // { dg-error "forming reference to qualified function" "" { target *-*-* } 0 } - -// { dg-prune-output "inconsistent deduction for auto return type" } // PR111484
On Fri, 2 Aug 2024 at 23:43, Patrick Palka <ppalka@redhat.com> wrote: > > On Fri, 2 Aug 2024, Patrick Palka wrote: > > > On Fri, 2 Aug 2024, Jonathan Wakely wrote: > > > > > This isn't properly tested so I'm not pushing it, but I'm sharing it now > > > for comment. > > > > > > -- >8 -- > > > > > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > > > replaces the deduced return type of std::forward_like with a type trait > > > to compute that type. > > > > > > libstdc++-v3/ChangeLog: > > > > > > * include/bits/move.h (__forward_like_impl): New metafunction to > > > compute the return type of std::forward_like. > > > (forward_like, __like_t): Use it. > > > * testsuite/20_util/forward_like/2_neg.cc: Adjust expected > > > errors. > > > --- > > > libstdc++-v3/include/bits/move.h | 45 +++++++++---------- > > > .../testsuite/20_util/forward_like/2_neg.cc | 2 +- > > > 2 files changed, 23 insertions(+), 24 deletions(-) > > > > > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > > > index bb200c95964..15b7cd07fce 100644 > > > --- a/libstdc++-v3/include/bits/move.h > > > +++ b/libstdc++-v3/include/bits/move.h > > > @@ -88,31 +88,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > #if __glibcxx_forward_like // C++ >= 23 > > > template<typename _Tp, typename _Up> > > > - [[nodiscard]] > > > - constexpr decltype(auto) > > > - forward_like(_Up&& __x) noexcept > > > - { > > > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > > > - > > > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > > > - { > > > - using _Up2 = remove_reference_t<_Up>; > > > - if constexpr (__as_rval) > > > - return static_cast<const _Up2&&>(__x); > > > - else > > > - return static_cast<const _Up2&>(__x); > > > - } > > > - else > > > - { > > > - if constexpr (__as_rval) > > > - return static_cast<remove_reference_t<_Up>&&>(__x); > > > - else > > > - return static_cast<_Up&>(__x); > > > - } > > > - } > > > + struct __forward_like_impl > > > + { > > > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_reference) > > > + template<typename _Xp> using __remove_ref_t = __remove_reference(_Xp); > > > +#else > > > + template<typename _Xp> using __remove_ref_t = remove_reference_t<_Xp>; > > > +#endif > > > + template<typename _Xp, typename _Yp> > > > + using _Copy_const = __conditional_t<is_const_v<_Xp>, const _Yp, _Yp>; > > > + template<typename _Xp, typename _Yp> > > > + using _Override_ref = __conditional_t<is_rvalue_reference_v<_Xp>, > > > + __remove_ref_t<_Yp>&&, _Yp&>; > > > > Moving these nested alias templates out to namespace scope improves > > compile time and memory usage of forward_like by about 25% (since nested > > templates are more expensive to instantiate). > > > > However, the following implementation using a single partially > > specialized class template is a further 20% faster and uses around > > 15% less memory: > > > > -- >8 -- > > > > Subject: [PATCH] libstdc++: use concrete return type for std::forward_like > > > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > > inverts the relationship between forward_like and __like_t, so that > > forward_like is defined in terms of __like_t and with a concrete return > > type. __like_t in turn is defined via partial specializations that > > performs case analysis on the const- and reference-ness of T. > > > > This turns out to be more SFINAE friendly and significantly cheaper > > to compile than the previous implementation. > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/move.h (__like_impl): New metafunction. > > (__like_t): Redefine in terms of __like_impl. > > (forward_like): Redefine in terms of __like_t. > > * testsuite/20_util/forward_like/2_neg.cc: Don't expect > > error outside the immediate context anymore. > > --- > > libstdc++-v3/include/bits/move.h | 46 +++++++++---------- > > .../testsuite/20_util/forward_like/2_neg.cc | 6 +-- > > 2 files changed, 25 insertions(+), 27 deletions(-) > > > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > > index bb200c95964..eaf4280d7ec 100644 > > --- a/libstdc++-v3/include/bits/move.h > > +++ b/libstdc++-v3/include/bits/move.h > > @@ -88,31 +88,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > #if __glibcxx_forward_like // C++ >= 23 > > template<typename _Tp, typename _Up> > > - [[nodiscard]] > > - constexpr decltype(auto) > > - forward_like(_Up&& __x) noexcept > > - { > > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > > - > > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > > - { > > - using _Up2 = remove_reference_t<_Up>; > > - if constexpr (__as_rval) > > - return static_cast<const _Up2&&>(__x); > > - else > > - return static_cast<const _Up2&>(__x); > > - } > > - else > > - { > > - if constexpr (__as_rval) > > - return static_cast<remove_reference_t<_Up>&&>(__x); > > - else > > - return static_cast<_Up&>(__x); > > - } > > - } > > + struct __like_impl; // _Tp must be a reference and _Up an lvalue reference > > + > > + template<typename _Tp, typename _Up> > > + struct __like_impl<_Tp&, _Up&> > > + { using type = _Up&; }; > > + > > + template<typename _Tp, typename _Up> > > + struct __like_impl<const _Tp&, _Up&> > > + { using type = const _Up&; }; > > + > > + template<typename _Tp, typename _Up> > > + struct __like_impl<_Tp&&, _Up&> > > + { using type = _Up&&; }; > > + > > + template<typename _Tp, typename _Up> > > + struct __like_impl<const _Tp&&, _Up&> > > + { using type = const _Up&&; }; > > > > template<typename _Tp, typename _Up> > > - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); > > + using __like_t = __like_impl<_Tp&&, _Up&>; > > Oops, this patch I sent has a bug, this should say > typename __like_impl<_Tp&&, _Up&>::type. (The patch I > actually benchmarked didn't have this bug, phew :)) > > Lightly tested, full testing in progress. This is a really smart implementation. OK for trunk if it passes all tests. And for gcc-14 after a bit of time on trunk. > > -- >8 -- > > Subject: [PATCH] libstdc++: use concrete return type for std::forward_like > > Inspired by https://github.com/llvm/llvm-project/issues/101614 this > inverts the relationship between forward_like and __like_t, so that > forward_like is defined in terms of __like_t and with a concrete return > type. __like_t in turn is defined via partial specializations that > performs case analysis on the const- and reference-ness of T. > > This turns out to be more SFINAE friendly and significantly cheaper > to compile than the previous implementation. > > libstdc++-v3/ChangeLog: > > * include/bits/move.h (__like_impl): New metafunction. > (__like_t): Redefine in terms of __like_impl. > (forward_like): Redefine in terms of __like_t. > * testsuite/20_util/forward_like/2_neg.cc: Don't expect > error outside the immediate context anymore. > --- > libstdc++-v3/include/bits/move.h | 46 +++++++++---------- > .../testsuite/20_util/forward_like/2_neg.cc | 6 +-- > 2 files changed, 25 insertions(+), 27 deletions(-) > > diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h > index bb200c95964..caab3992e4a 100644 > --- a/libstdc++-v3/include/bits/move.h > +++ b/libstdc++-v3/include/bits/move.h > @@ -88,31 +88,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > #if __glibcxx_forward_like // C++ >= 23 > template<typename _Tp, typename _Up> > - [[nodiscard]] > - constexpr decltype(auto) > - forward_like(_Up&& __x) noexcept > - { > - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; > - > - if constexpr (is_const_v<remove_reference_t<_Tp>>) > - { > - using _Up2 = remove_reference_t<_Up>; > - if constexpr (__as_rval) > - return static_cast<const _Up2&&>(__x); > - else > - return static_cast<const _Up2&>(__x); > - } > - else > - { > - if constexpr (__as_rval) > - return static_cast<remove_reference_t<_Up>&&>(__x); > - else > - return static_cast<_Up&>(__x); > - } > - } > + struct __like_impl; // _Tp must be a reference and _Up an lvalue reference > + > + template<typename _Tp, typename _Up> > + struct __like_impl<_Tp&, _Up&> > + { using type = _Up&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<const _Tp&, _Up&> > + { using type = const _Up&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<_Tp&&, _Up&> > + { using type = _Up&&; }; > + > + template<typename _Tp, typename _Up> > + struct __like_impl<const _Tp&&, _Up&> > + { using type = const _Up&&; }; > > template<typename _Tp, typename _Up> > - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); > + using __like_t = typename __like_impl<_Tp&&, _Up&>::type; > + > + template<typename _Tp, typename _Up> > + constexpr __like_t<_Tp, _Up> > + forward_like(_Up&& __x) noexcept > + { return static_cast<__like_t<_Tp, _Up>>(__x); } > #endif > > /** > diff --git a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc > index ff835af1915..5dafa419a7e 100644 > --- a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc > +++ b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc > @@ -2,9 +2,7 @@ > > #include <utility> > > -auto x1 = std::forward_like<void>(1); // { dg-error "here" } > +auto x1 = std::forward_like<void>(1); // { dg-error "no match" } > // { dg-error "forming reference to void" "" { target *-*-* } 0 } > -auto x2 = std::forward_like<void()const>(1); // { dg-error "here" } > +auto x2 = std::forward_like<void()const>(1); // { dg-error "no match" } > // { dg-error "forming reference to qualified function" "" { target *-*-* } 0 } > - > -// { dg-prune-output "inconsistent deduction for auto return type" } // PR111484 > -- > 2.46.0.39.g891ee3b9db >
diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h index bb200c95964..15b7cd07fce 100644 --- a/libstdc++-v3/include/bits/move.h +++ b/libstdc++-v3/include/bits/move.h @@ -88,31 +88,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __glibcxx_forward_like // C++ >= 23 template<typename _Tp, typename _Up> - [[nodiscard]] - constexpr decltype(auto) - forward_like(_Up&& __x) noexcept - { - constexpr bool __as_rval = is_rvalue_reference_v<_Tp&&>; - - if constexpr (is_const_v<remove_reference_t<_Tp>>) - { - using _Up2 = remove_reference_t<_Up>; - if constexpr (__as_rval) - return static_cast<const _Up2&&>(__x); - else - return static_cast<const _Up2&>(__x); - } - else - { - if constexpr (__as_rval) - return static_cast<remove_reference_t<_Up>&&>(__x); - else - return static_cast<_Up&>(__x); - } - } + struct __forward_like_impl + { +#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_reference) + template<typename _Xp> using __remove_ref_t = __remove_reference(_Xp); +#else + template<typename _Xp> using __remove_ref_t = remove_reference_t<_Xp>; +#endif + template<typename _Xp, typename _Yp> + using _Copy_const = __conditional_t<is_const_v<_Xp>, const _Yp, _Yp>; + template<typename _Xp, typename _Yp> + using _Override_ref = __conditional_t<is_rvalue_reference_v<_Xp>, + __remove_ref_t<_Yp>&&, _Yp&>; + using type = _Override_ref<_Tp&&, _Copy_const<__remove_ref_t<_Tp>, + __remove_ref_t<_Up>>>; + }; template<typename _Tp, typename _Up> - using __like_t = decltype(std::forward_like<_Tp>(std::declval<_Up>())); + using __like_t = typename __forward_like_impl<_Tp, _Up>::type; + + template<typename _Tp, typename _Up> + [[nodiscard]] + constexpr __like_t<_Tp, _Up> + forward_like(_Up&& __x) noexcept + { return static_cast<__like_t<_Tp, _Up>>(__x); } #endif /** diff --git a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc index ff835af1915..f6e98420a9a 100644 --- a/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc +++ b/libstdc++-v3/testsuite/20_util/forward_like/2_neg.cc @@ -7,4 +7,4 @@ auto x1 = std::forward_like<void>(1); // { dg-error "here" } auto x2 = std::forward_like<void()const>(1); // { dg-error "here" } // { dg-error "forming reference to qualified function" "" { target *-*-* } 0 } -// { dg-prune-output "inconsistent deduction for auto return type" } // PR111484 +// { dg-prune-output "no matching function" }