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
-- 
2.46.0.39.g891ee3b9db
#include <utility>

template<class T, class U>
void f(U& u) {
  std::forward_like<T&>(u);
  std::forward_like<const T&>(u);
  std::forward_like<T&&>(u);
  std::forward_like<const T&&>(u);

  std::forward_like<T&>((const U&)u);
  std::forward_like<const T&>((const U&)u);
  std::forward_like<T&&>((const U&)u);
  std::forward_like<const T&&>((const U&)u);

  std::forward_like<T&>((U&&)u);
  std::forward_like<const T&>((U&&)u);
  std::forward_like<T&&>((U&&)u);
  std::forward_like<const T&&>((U&&)u);

  std::forward_like<T&>((const U&&)u);
  std::forward_like<const T&>((const U&&)u);
  std::forward_like<T&&>((const U&&)u);
  std::forward_like<const T&&>((const U&&)u);
};

template<int N>
struct A { };

struct B { };

template<int N>
void g(B b) {
  f<A<N+0>>(b);
  f<A<N+1>>(b);
  f<A<N+2>>(b);
  f<A<N+3>>(b);
  f<A<N+4>>(b);
  f<A<N+5>>(b);
  f<A<N+6>>(b);
  f<A<N+7>>(b);
  f<A<N+8>>(b);
  f<A<N+9>>(b);
}

template<int N>
void h(B b) {
  g<N>(b);
  g<N+10>(b);
  g<N+20>(b);
  g<N+30>(b);
  g<N+40>(b);
  g<N+50>(b);
  g<N+60>(b);
  g<N+70>(b);
  g<N+80>(b);
  g<N+90>(b);
}

template void h<0>(B);
template void h<100>(B);
template void h<200>(B);
template void h<300>(B);
template void h<400>(B);
template void h<500>(B);
template void h<600>(B);
template void h<700>(B);
template void h<800>(B);
template void h<900>(B);

Reply via email to