[PATCH] libstdc++: Do not use deduced return type for std::forward_like
Patrick Palka
ppalka@redhat.com
Fri Aug 2 22:43:41 GMT 2024
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
--
2.46.0.39.g891ee3b9db
More information about the Libstdc++
mailing list