This patch uses the additional provisions from LWG 4264, to avoid double
indirection when function_ref is constructed from function_ref with
compatible signature.

The details of compatible signatures follows the move_only_function
as described in r16-617-g708d40ff109c6e49d02b684a368571722a160af8.
However, due the non-owning nature of function_ref, it operator()
is always const-quaulified, that means that we can constructor a
function_ref<Ret(Args...) const> from function_ref<Ret(Args...)>,
even if the underyling target will be mutated.

The implementations moves the _M_ptrs  members to newly defined base
class __polyfunc::_Ref_base. This allows us to reuse existing __base_of
and __invoker_of accessor in the implementation (after befriending them).
The accessors functions are also now marked as constexpr.
Furthermore we move _M_init function there, making it instantiations
independent from callback signature.

        PR libstdc++/119126

libstdc++-v3/ChangeLog:

        * include/bits/cpyfunc_impl.h: (__polyfunc::__invoker_of)
        (__polyfunc::_base_of): Mark as constexpr.
        * include/bits/funcref_impl.h (std::function_ref): Add base class
        of type__polyfunc::_Ref_base. Befriend __invoker_of, __base_of,
        __is_invoker_convertible.
        (function_ref::_Base): Define.
        (function_ref::_M_init, function_ref::_M_ptrs): Move to base class.
        (function_ref::function_ref(_Fn&&)): Handle specializations of
        function_ref.
        (function_ref::function_ref): Init base class before _M_invoke
        consistently.
        * include/bits/funcwrap.h : (__polyfunc::__invoker_of)
        (__polyfunc::_base_of): Mark as constexpr.
        (__polyfunc::__is_function_ref_v, __polyfunc::_Ref_base): Define.
        * include/bits/mofunc_impl.h: (__polyfunc::__invoker_of)
        (__polyfunc::_base_of): Mark as constexpr.
        * testsuite/20_util/function_ref/conv.cc: Updated test to illustrate
        that double indirection is avoided. Add constexpr tests.
---
 libstdc++-v3/include/bits/cpyfunc_impl.h      |  4 +-
 libstdc++-v3/include/bits/funcref_impl.h      | 48 ++++++++++++-------
 libstdc++-v3/include/bits/funcwrap.h          | 26 +++++++++-
 libstdc++-v3/include/bits/mofunc_impl.h       |  4 +-
 .../testsuite/20_util/function_ref/conv.cc    | 27 ++++++++---
 5 files changed, 80 insertions(+), 29 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h 
b/libstdc++-v3/include/bits/cpyfunc_impl.h
index bc44cd3e313..7ba74ed8a8d 100644
--- a/libstdc++-v3/include/bits/cpyfunc_impl.h
+++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
@@ -248,11 +248,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typename _Invoker::__storage_func_t _M_invoke = nullptr;
 
       template<typename _Func>
-       friend auto&
+       friend constexpr auto&
        __polyfunc::__invoker_of(_Func&) noexcept;
 
       template<typename _Func>
-       friend auto&
+       friend constexpr auto&
        __polyfunc::__base_of(_Func&) noexcept;
 
       template<typename _Dst, typename _Src>
diff --git a/libstdc++-v3/include/bits/funcref_impl.h 
b/libstdc++-v3/include/bits/funcref_impl.h
index 1e19866035f..f3ec765b953 100644
--- a/libstdc++-v3/include/bits/funcref_impl.h
+++ b/libstdc++-v3/include/bits/funcref_impl.h
@@ -67,7 +67,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Res, typename... _ArgTypes, bool _Noex>
     class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
                       noexcept(_Noex)>
+    : __polyfunc::_Ref_base
     {
+      using _Base = __polyfunc::_Ref_base;
       using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
       using _Signature = _Invoker::_Signature;
 
@@ -85,8 +87,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        function_ref(_Fn* __fn) noexcept
        {
          __glibcxx_assert(__fn != nullptr);
-         _M_invoke = _Invoker::template _S_ptrs<_Fn*>();
          _M_init(__fn);
+         _M_invoke = _Invoker::template _S_ptrs<_Fn*>();
        }
 
       /// Target and bound object is object referenced by parameter.
@@ -102,8 +104,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        constexpr
        function_ref(_Fn&& __f) noexcept
        {
-         _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
-         _M_init(std::addressof(__f));
+         using _Vd = remove_cv_t<_Vt>;
+         if constexpr (__is_function_ref_v<_Vd>
+              && __polyfunc::__is_invoker_convertible<_Vd, function_ref>())
+           {
+             _Base::operator=(__polyfunc::__base_of(__f));
+             _M_invoke = __polyfunc::__invoker_of(__f);
+           }
+         else
+           {
+             using _Tr = _Vt _GLIBCXX_MOF_CV&;
+             _M_init(std::addressof(__f));
+             _M_invoke = _Invoker::template _S_ptrs<_Tr>();
+           }
        }
 
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
@@ -118,8 +131,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
            static_assert(__fn != nullptr);
 
-         _M_invoke = &_Invoker::template _S_nttp<__fn>;
          _M_ptrs._M_obj = nullptr;
+         _M_invoke = &_Invoker::template _S_nttp<__fn>;
        }
 
       /// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)).
@@ -135,13 +148,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            static_assert(__fn != nullptr);
 
          using _Tr = _Td _GLIBCXX_MOF_CV&;
+         _M_init(std::addressof(__ref));
          if constexpr (is_member_pointer_v<_Fn> && is_lvalue_reference_v<_Tr>)
            // N.B. invoking member pointer on lvalue produces the same effects,
            // as invoking it on pointer to that lvalue.
            _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td 
_GLIBCXX_MOF_CV>;
          else
            _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>;
-         _M_init(std::addressof(__ref));
        }
 
       /// Target object is equivalent to std::bind_front<_fn>(__ptr).
@@ -157,8 +170,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          if constexpr (is_member_pointer_v<_Fn>)
            __glibcxx_assert(__ptr != nullptr);
 
-         _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td 
_GLIBCXX_MOF_CV>;
          _M_init(__ptr);
+         _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td 
_GLIBCXX_MOF_CV>;
        }
 
       template<typename _Tp>
@@ -178,18 +191,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _M_invoke(_M_ptrs, std::forward<_ArgTypes>(__args)...); }
 
     private:
-      template<typename _Tp>
-       constexpr void
-       _M_init(_Tp* __ptr) noexcept
-       {
-         if constexpr (is_function_v<_Tp>)
-           _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr);
-         else
-           _M_ptrs._M_obj = __ptr;
-       }
-
       typename _Invoker::__ptrs_func_t _M_invoke;
-      __polyfunc::_Ptrs _M_ptrs;
+
+      template<typename _Func>
+       friend constexpr auto&
+       __polyfunc::__invoker_of(_Func&) noexcept;
+
+      template<typename _Func>
+       friend constexpr auto&
+       __polyfunc::__base_of(_Func&) noexcept;
+
+      template<typename _Dst, typename _Src>
+       friend consteval bool
+       __polyfunc::__is_invoker_convertible() noexcept;
     };
 
 #undef _GLIBCXX_MOF_CV
diff --git a/libstdc++-v3/include/bits/funcwrap.h 
b/libstdc++-v3/include/bits/funcwrap.h
index cf261bcd4c8..1c1ba637689 100644
--- a/libstdc++-v3/include/bits/funcwrap.h
+++ b/libstdc++-v3/include/bits/funcwrap.h
@@ -205,12 +205,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
      using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>, 
__param_t<_Args>...>;
 
    template<typename _Func>
-     auto&
+     constexpr auto&
      __invoker_of(_Func& __f) noexcept
      { return __f._M_invoke; }
 
    template<typename _Func>
-     auto&
+     constexpr auto&
      __base_of(_Func& __f) noexcept
      { return static_cast<__like_t<_Func&, typename _Func::_Base>>(__f); }
 
@@ -518,6 +518,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// @cond undocumented
   namespace __polyfunc
   {
+    struct _Ref_base
+    {
+      template<typename _Tp>
+       constexpr void
+       _M_init(_Tp* __ptr) noexcept
+       {
+         if constexpr (is_function_v<_Tp>)
+           _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr);
+         else
+           _M_ptrs._M_obj = __ptr;
+       }
+
+      _Ptrs _M_ptrs;
+    };
+
     template<typename _Sig>
       struct __skip_first_arg;
 
@@ -556,6 +571,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       -> function_ref<
           remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn, 
_Tp&>())>>;
 
+  /// @cond undocumented
+  template<typename _Tp>
+    constexpr bool __is_function_ref_v = false;
+  template<typename _Tp>
+    constexpr bool __is_function_ref_v<function_ref<_Tp>> = true;
+  /// @endcond
+
 #endif // __glibcxx_function_ref
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/bits/mofunc_impl.h 
b/libstdc++-v3/include/bits/mofunc_impl.h
index 1ceb910e815..017678d1d51 100644
--- a/libstdc++-v3/include/bits/mofunc_impl.h
+++ b/libstdc++-v3/include/bits/mofunc_impl.h
@@ -232,11 +232,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typename _Invoker::__storage_func_t _M_invoke = nullptr;
 
       template<typename _Func>
-       friend auto&
+       friend constexpr auto&
        __polyfunc::__invoker_of(_Func&) noexcept;
 
       template<typename _Func>
-       friend auto&
+       friend constexpr auto&
        __polyfunc::__base_of(_Func&) noexcept;
 
       template<typename _Dst, typename _Src>
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc 
b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
index 49ff0a78829..d4bf15c03a9 100644
--- a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
@@ -20,8 +20,8 @@ struct CountedArg
 };
 CountedArg const c;
 
-// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref,
-// so we make extra copies of arguments.
+// LWG 4264 modify [func.wrap.general] p2 to also  cover funciton_ref,
+// so we make avoid extra copies in some situations.
 
 void
 test01()
@@ -38,14 +38,14 @@ test01()
   VERIFY( r2c(c) == 2 );
 
   std::function_ref<int(CountedArg) const> r3r(r1);
-  VERIFY( r3r(c) == 2 );
+  VERIFY( r3r(c) == 1 );
   std::function_ref<int(CountedArg) const> r3m(m1);
   VERIFY( r3m(c) == 2 );
   std::function_ref<int(CountedArg) const> r3c(c1);
   VERIFY( r3c(c) == 2 );
 
   std::function_ref<int(CountedArg)> r4r(r1);
-  VERIFY( r4r(c) == 2 );
+  VERIFY( r4r(c) == 1 );
   std::function_ref<int(CountedArg)> r4m(m1);
   VERIFY( r4m(c) == 2 );
   std::function_ref<int(CountedArg)> r4c(c1);
@@ -94,7 +94,7 @@ test03()
   // Call const overload as std::function_ref<int(CountedArg) const>
   // inside std::function_ref<int(CountedArg)> would do.
   std::function_ref<int(CountedArg)> r2(r1);
-  VERIFY( r2(c) == 1002 );
+  VERIFY( r2(c) == 1001 );
   std::move_only_function<int(CountedArg)> m2(r1);
   VERIFY( m2(c) == 1002 );
 
@@ -103,7 +103,7 @@ test03()
   std::function_ref<int(CountedArg)> r3(f);
   VERIFY( r3(c) == 1 );
   std::function_ref<int(CountedArg) const> r4(r3);
-  VERIFY( r4(c) == 2 );
+  VERIFY( r4(c) == 1 );
   std::move_only_function<int(CountedArg) const> m4(r3);
   VERIFY( m4(c) == 2 );
 }
@@ -142,6 +142,18 @@ test05()
   VERIFY( f2(c) == 2 );
 }
 
+constexpr bool
+test07()
+{
+  auto f = [](int x) noexcept { return x; };
+  std::function_ref<int(int) const noexcept> rf(f);
+
+  std::function_ref<int(int) const noexcept> rr1(rf);
+  std::function_ref<int(int)> rr2(rf);
+  std::function_ref<int(long)> rr3(rf);
+  return true;
+};
+
 int main()
 {
   test01();
@@ -149,4 +161,7 @@ int main()
   test03();
   test04();
   test05();
+  test07();
+
+  static_assert( test07() );
 }
-- 
2.49.0

Reply via email to