On Wed, May 6, 2026 at 12:06 PM Jonathan Wakely <[email protected]> wrote:

> On Mon, 13 Apr 2026 at 15:22 +0200, Tomasz Kamiński wrote:
> >This implements the P3961R1: Less double indirection in function_ref.
> >
> >This patch uses provision provided by the paper for implementations, and
> >expands the set of the compatible signatures to include ParamType&& and
> ParamType,
> >and by value return types that differs only in cv-qualifiers. This follows
> >the move_only_function approach from the r16-617-g708d40ff109c6e.
> Futhermore the
> >optimization is also applied when function_ref<Ret(Args...) const> is
> constructed
> >from function_ref<Ret(Args...)>, even if the underyling target is be
> mutated by
> >the call.
> >
> >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,
> >_Ref_base default constructor initializes the _M_ptrs._M_obj to nullptr,
> >and _M_init function is transfered to it, making it instantiations
> >independent from callback signature.
> >
> >To check signature compatiblity required by standard for assignment,  new
> >__is_funcref_assignable function is used, and _ArgsSignature and
> _TargetQuals
> >member typedef are defined in function_ref. To avoid confusion between
> adjusted
> >and specified signature, _Signature typedef is removed from all wrappers,
> and
> >__is_invoker_convertible is updated to use _Invoker::_Signature instead.
> >
> >Per SG-10 guidance __cpp_lib_function_ref feature test macro is updated to
> >202604, differntiating from P3948R1 paper accepted at the same time.
> >
> >       PR libstdc++/119126
> >
> >libstdc++-v3/ChangeLog:
> >
> >       * include/bits/funcwrap.h : (__polyfunc::__invoker_of):
> >       Updated to use _Invoker::_Signature and mark as constexpr.
> >       (__polyfunc::_base_of): Mark as constexpr.
> >       (__std:::__is_function_ref_v, __polyfunc::_Ref_base)
> >       (__polyfunc::__is_funcref_assignable): Define.
> >       * include/bits/funcref_impl.h (std::function_ref): Add base class
> >       of type__polyfunc::_Ref_base. Befriend __invoker_of, __base_of,
> >       __is_invoker_convertible, __is_invoker_convertible.
> >       (function_ref::_Base): Define.
> >       (function_ref::_M_init, function_ref::_M_ptrs): Move to base class.
> >       (function_ref::function_ref(_Fn&&), function_ref::operator=):
> Handle
> >       specializations of function_ref with compatible signatures.
> >       (function_ref::function_ref): Init base class before _M_invoke
> >       consistently, and remove setting of _M_nullptr.
> >       * include/bits/cpyfunc_impl.h (copyable_function): Udpdated friend
> >       declarations.
> >       (copyable_function::_Signature): Remove.
> >       * include/bits/mofunc_impl.h (move_only_function): Udpdated friend
> >       declarations.
> >       (move_only_function::_Signature): Remove.
> >       * include/bits/version.def (function_ref): Bump to 202604.
> >       * include/bits/version.h: Regnerate.
> >       * testsuite/20_util/function_ref/cons.cc: Updated checked FTM
> value.
> >       * testsuite/20_util/function_ref/conv.cc: Updated test to
> illustrate
> >       that double indirection is avoided.
> >       * testsuite/20_util/function_ref/dangling.cc: Test for initializing
> >       from function_ref with compatible signature.
> >       * testsuite/20_util/function_ref/dangling_neg.cc: Test for
> initializing
> >       from function_ref with incompatible signature.
> >---
> >I am using the provisions to skip indirection given by standard to full
> extents,
> >including the const to non-const case, that works due refrence to
> reference
> >semantics:
> >  std::function_ref<void()> f(x); // may mutate x
> >  std::function_ref<void() const> w(f); // f is const callable
> >However, I think this is aligned with general LEWG vote, that people
> should
> >not rely this sematnic.
> >
> >Testing on x86_64-linux. *function_ref* passed with c++26 mode and
> modules.
> >OK for trunk when all test passes?
> >
> > libstdc++-v3/include/bits/cpyfunc_impl.h      |   7 +-
> > libstdc++-v3/include/bits/funcref_impl.h      |  58 ++++++----
> > libstdc++-v3/include/bits/funcwrap.h          |  52 ++++++++-
> > libstdc++-v3/include/bits/mofunc_impl.h       |   7 +-
> > libstdc++-v3/include/bits/version.def         |   3 +-
> > libstdc++-v3/include/bits/version.h           |   4 +-
> > .../testsuite/20_util/function_ref/cons.cc    |   2 +-
> > .../testsuite/20_util/function_ref/conv.cc    | 103 ++++++++++++++----
> > .../20_util/function_ref/dangling.cc          |  17 ++-
> > .../20_util/function_ref/dangling_neg.cc      |   8 ++
> > 10 files changed, 196 insertions(+), 65 deletions(-)
> >
> >diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h
> b/libstdc++-v3/include/bits/cpyfunc_impl.h
> >index f1918ddf87a..580295a5d63 100644
> >--- a/libstdc++-v3/include/bits/cpyfunc_impl.h
> >+++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
> >@@ -70,7 +70,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       using _Base = __polyfunc::_Cpy_base;
> >       using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
> >-      using _Signature = _Invoker::_Signature;
> >
> >       template<typename _Tp>
> >       using __callable
> >@@ -252,14 +251,14 @@ _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>
> >+      template<typename _Src, typename _Dst>
> >       friend consteval bool
> >       __polyfunc::__is_invoker_convertible() noexcept;
> >     };
> >diff --git a/libstdc++-v3/include/bits/funcref_impl.h
> b/libstdc++-v3/include/bits/funcref_impl.h
> >index 9fcab570803..151f326f017 100644
> >--- a/libstdc++-v3/include/bits/funcref_impl.h
> >+++ b/libstdc++-v3/include/bits/funcref_impl.h
> >@@ -48,6 +48,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                             noexcept(_Noex)>
> >       { using type = _Ret(_Args...) noexcept(_Noex); };
> >   } // namespace __polyfunc
> >+
> >   /// @endcond
> >
> >   /**
> >@@ -67,13 +68,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   template<typename _Res, typename... _ArgTypes, bool _Noex>
> >     class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
> >                      noexcept(_Noex)>
> >+    : __polyfunc::_Ref_base
> >     {
> >       static_assert(
> >       (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) &&
> ...),
> >       "each parameter type must be a complete class");
> >
> >+      using _Base = __polyfunc::_Ref_base;
> >       using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
> >-      using _Signature = _Invoker::_Signature;
> >+      using _ArgsSignature = _Res(_ArgTypes...) noexcept(_Noex);
> >+      using _TargetQuals = int _GLIBCXX_MOF_CV&;
> >
> >       // [func.wrap.ref.ctor]/1 is-invokable-using
> >       template<typename... _Tps>
> >@@ -89,8 +93,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.
> >@@ -108,21 +112,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       {
> >         using _Fd = remove_cv_t<_Vt>;
> >         if constexpr (__is_std_op_wrapper<_Fd>)
> >-          {
> >-            _M_invoke = _Invoker::template _S_nttp<_Fd{}>;
> >-            _M_ptrs._M_obj = nullptr;
> >-          }
> >+          _M_invoke = _Invoker::template _S_nttp<_Fd{}>;
> >         else if constexpr (requires (_ArgTypes&&... __args) {
> >                   _Fd::operator()(std::forward<_ArgTypes>(__args)...);
> >                 })
> >+          _M_invoke = _Invoker::template _S_static<_Fd>;
> >+        else if constexpr (__is_function_ref_v<_Fd>
> >+             && __polyfunc::__is_invoker_convertible<_Fd,
> function_ref>())
> >           {
> >-            _M_invoke = _Invoker::template _S_static<_Fd>;
> >-            _M_ptrs._M_obj = nullptr;
> >+            _Base::operator=(__polyfunc::__base_of(__f));
> >+            _M_invoke = __polyfunc::__invoker_of(__f);
> >           }
> >           else
> >           {
> >-            _M_invoke = _Invoker::template _S_ptrs<_Vt
> _GLIBCXX_MOF_CV&>();
> >             _M_init(std::addressof(__f));
> >+            _M_invoke = _Invoker::template _S_ptrs<_Vt
> _GLIBCXX_MOF_CV&>();
> >           }
> >       }
> >
> >@@ -146,7 +150,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           static_assert(__fn != nullptr);
> >
> >         _M_invoke = &_Invoker::template _S_nttp<__fn>;
> >-        _M_ptrs._M_obj = nullptr;
> >       }
> >
> >       /// Target object is equivalent to
> std::bind_front<_fn>(std::ref(__ref)).
> >@@ -162,6 +165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
> >           static_assert(__fn != nullptr);
> >
> >+        _M_init(std::addressof(__ref));
> >         if constexpr (is_member_pointer_v<_Fn>
> >                         && same_as<_Td, typename
> __inv_unwrap<_Td>::type>)
> >           // N.B. invoking member pointer on lvalue produces the same
> effects,
> >@@ -169,7 +173,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td
> _GLIBCXX_MOF_CV>;
> >         else
> >           _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Td
> _GLIBCXX_MOF_CV&>;
> >-        _M_init(std::addressof(__ref));
> >       }
> >
> >       /// Target object is equivalent to std::bind_front<_fn>(__ptr).
> >@@ -185,13 +188,15 @@ _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>
> >-      requires (!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>)
> >+      requires (!is_same_v<_Tp, function_ref>)
> >+               && (!is_pointer_v<_Tp>)
> >                && (!__is_constant_wrapper_v<_Tp>)
> >+               && (!__polyfunc::__is_funcref_assignable<function_ref,
> _Tp>())
> >       function_ref&
> >       operator=(_Tp) = delete;
> >
> >@@ -206,18 +211,23 @@ _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 _Src, typename _Dst>
> >+      friend consteval bool
> >+      __polyfunc::__is_invoker_convertible() noexcept;
> >+
> >+      template<typename _Dst, typename _Src>
> >+        friend consteval bool
> >+        __polyfunc::__is_funcref_assignable() noexcept;
> >     };
> >
> > #undef _GLIBCXX_MOF_CV
> >diff --git a/libstdc++-v3/include/bits/funcwrap.h
> b/libstdc++-v3/include/bits/funcwrap.h
> >index b835e075295..69ae0c131ef 100644
> >--- a/libstdc++-v3/include/bits/funcwrap.h
> >+++ b/libstdc++-v3/include/bits/funcwrap.h
> >@@ -231,12 +231,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);
> }
> >
> >@@ -244,9 +244,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      consteval bool
> >      __is_invoker_convertible() noexcept
> >      {
> >-       if constexpr (requires { typename _Src::_Signature; })
> >-       return is_convertible_v<typename _Src::_Signature,
> >-                               typename _Dst::_Signature>;
> >+       if constexpr (requires { typename _Src::_Invoker::_Signature; })
> >+       return is_convertible_v<typename _Src::_Invoker::_Signature,
> >+                               typename _Dst::_Invoker::_Signature>;
> >        else
> >        return false;
> >      }
> >@@ -539,6 +539,25 @@ _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;
> >+      }
> >+
> >+      constexpr
> >+      _Ref_base() noexcept
> >+      { _M_ptrs._M_obj = nullptr; }
> >+
> >+      _Ptrs _M_ptrs;
> >+    };
> >+
> >     template<typename _Sig>
> >       struct __skip_first_arg;
> >
> >@@ -585,6 +604,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     function_ref(constant_wrapper<__cwfn, _Fn>, _Tp&&)
> >       -> function_ref<remove_pointer_t<_SignaturePtr>>;
> >
> >+  /// @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;
> >+
> >+  /// @cond undocumented
>
> This @cond is not matched with an @endcond which will confuse Doxygen
> (https://github.com/doxygen/doxygen/issues/12071).
>
> It lkooks fine apart from that. I like the _TargetQuals typedef to
> check for convertibility.
>
Yes, my prototype implementation declared some template and partial
specialization for
combinations of cv and ref qualified function signatures, which became
annoying.
This one is much simpler, and can be easily expanded to support ref
qualifiers.

>
> OK for trunk.
>
>
> >+  namespace __polyfunc
> >+  {
> >+    template<typename _Dst, typename _Src>
> >+      consteval bool
> >+      __is_funcref_assignable() noexcept
> >+      {
> >+      if constexpr (__is_function_ref_v<_Src>)
> >+        if constexpr (is_convertible_v<typename _Src::_ArgsSignature*,
> >+                                       typename _Dst::_ArgsSignature*>)
> >+          return is_convertible_v<typename _Dst::_TargetQuals,
> >+                                  typename _Src::_TargetQuals>;
> >+      return false;
> >+      }
> >+  } // namespace __polyfunc
> >+  /// @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 468e6855fc6..91a78bfca01 100644
> >--- a/libstdc++-v3/include/bits/mofunc_impl.h
> >+++ b/libstdc++-v3/include/bits/mofunc_impl.h
> >@@ -70,7 +70,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       using _Base = __polyfunc::_Mo_base;
> >       using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
> >-      using _Signature = _Invoker::_Signature;
> >
> >       template<typename _Tp>
> >       using __callable
> >@@ -236,14 +235,14 @@ _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>
> >+      template<typename _Src, typename _Dst>
> >       friend consteval bool
> >       __polyfunc::__is_invoker_convertible() noexcept;
> >     };
> >diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> >index b88d9c3483f..539dc5ee4eb 100644
> >--- a/libstdc++-v3/include/bits/version.def
> >+++ b/libstdc++-v3/include/bits/version.def
> >@@ -1962,9 +1962,10 @@ ftms = {
> >   // 202306 P0792R14 function_ref: a non-owning reference to a Callable
> >   // 202511 P3774R1  Rename std::nontype, and make it broadly useful
> >   // 202603 P3948R1  constant_wrapper is the only tool needed for
> passing...
> >+  // 202604 P3961R1  Less double indirection in function_ref (RU-220)
> >   name = function_ref;
> >   values = {
> >-    v = 202603;
> >+    v = 202604;
> >     cxxmin = 26;
> >   };
> > };
> >diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> >index 24ec0175e1e..57aa5baf5f9 100644
> >--- a/libstdc++-v3/include/bits/version.h
> >+++ b/libstdc++-v3/include/bits/version.h
> >@@ -2183,9 +2183,9 @@
> >
> > #if !defined(__cpp_lib_function_ref)
> > # if (__cplusplus >  202302L)
> >-#  define __glibcxx_function_ref 202603L
> >+#  define __glibcxx_function_ref 202604L
> > #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_function_ref)
> >-#   define __cpp_lib_function_ref 202603L
> >+#   define __cpp_lib_function_ref 202604L
> > #  endif
> > # endif
> > #endif /* !defined(__cpp_lib_function_ref) */
> >diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> >index e6c13e3e538..33f30099c54 100644
> >--- a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> >+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> >@@ -5,7 +5,7 @@
> >
> > #ifndef __cpp_lib_function_ref
> > # error "Feature-test macro for function_ref missing in <functional>"
> >-#elif __cpp_lib_function_ref != 202603L
> >+#elif __cpp_lib_function_ref != 202604L
> > # error "Feature-test macro for function_ref has wrong value in
> <functional>"
> > #endif
> >
> >diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> >index 7606d265f98..4d772580b39 100644
> >--- a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> >+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> >@@ -36,8 +36,33 @@ static_assert(
> std::is_same_v<std::function_ref<void(int const[5])>,
> > static_assert( std::is_same_v<std::function_ref<void(FuncType)>,
> >                             std::function_ref<void(FuncType*)>>);
> >
> >-// The C++26 [func.wrap.general] p2 does not currently cover
> funciton_ref,
> >-// so we make extra copies of arguments.
> >+// Compatible signatures per standard, function_ref is assingable
> >+static_assert( std::is_assignable_v<std::function_ref<int(long)
> noexcept>&,
> >+                                  std::function_ref<int(long) const
> noexcept>> );
> >+static_assert( std::is_assignable_v<std::function_ref<int(long) const>&,
> >+                                  std::function_ref<int(long) const
> noexcept>> );
> >+static_assert( std::is_assignable_v<std::function_ref<int(long)>&,
> >+                                  std::function_ref<int(long) const
> noexcept>> );
> >+
> >+// Incompatible signatures per standard,  not assignable
> >+static_assert( !std::is_assignable_v<std::function_ref<int(long) const>&,
> >+                                   std::function_ref<int(long)>> );
> >+static_assert( !std::is_assignable_v<std::function_ref<int(int)>&,
> >+                                   std::function_ref<int(long)>> );
> >+static_assert( !std::is_assignable_v<std::function_ref<long(long)>&,
> >+                                   std::function_ref<int(long)>> );
> >+static_assert( !std::is_assignable_v<std::function_ref<long(int)>&,
> >+                                   std::function_ref<int(long)>> );
> >+
> >+// Implementation-specific compatible signatures, function_ref is not
> assingable
> >+static_assert( !std::is_assignable_v<std::function_ref<int(CountedArg)>&,
> >+                                   std::function_ref<int(CountedArg&&)>>
> );
> >+static_assert( !std::is_assignable_v<std::function_ref<CountedArg()>&,
> >+                                   std::function_ref<const
> CountedArg(long)>> );
> >+static_assert( !std::is_assignable_v<std::function_ref<const
> CountedArg()>&,
> >+                                   std::function_ref<CountedArg(long)>>
> );
> >+static_assert( !std::is_assignable_v<std::function_ref<int(long) const>&,
> >+                                   std::function_ref<int(long)>> );
> >
> > void
> > test01()
> >@@ -54,26 +79,40 @@ 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 );
> >-  std::function_ref<int(CountedArg)> r4m(m1);
> >+  std::function_ref<int(CountedArg) noexcept> r4r(r1);
> >+  VERIFY( r4r(c) == 1 );
> >+  std::function_ref<int(CountedArg) noexcept> r4m(m1);
> >   VERIFY( r4m(c) == 2 );
> >-  std::function_ref<int(CountedArg)> r4c(c1);
> >+  std::function_ref<int(CountedArg) noexcept> r4c(c1);
> >   VERIFY( r4c(c) == 2 );
> >
> >+  std::function_ref<int(CountedArg)> r5r(r1);
> >+  VERIFY( r4r(c) == 1 );
> >+  std::function_ref<int(CountedArg)> r5m(m1);
> >+  VERIFY( r4m(c) == 2 );
> >+  std::function_ref<int(CountedArg)> r5c(c1);
> >+  VERIFY( r4c(c) == 2 );
> >+
> >+  r3r = r1;
> >+  VERIFY( r3r(c) == 1 );
> >+  r4r = r1;
> >+  VERIFY( r4r(c) == 1 );
> >+  r5r = r1;
> >+  VERIFY( r5r(c) == 1 );
> >+
> >   // Incompatible signatures
> >-  std::function_ref<long(CountedArg) const noexcept> r5r(r1);
> >-  VERIFY( r5r(c) == 2 );
> >-  std::function_ref<long(CountedArg) const noexcept> r5m(m1);
> >-  VERIFY( r5r(c) == 2 );
> >-  std::function_ref<long(CountedArg) const noexcept> r5c(c1);
> >-  VERIFY( r5r(c) == 2 );
> >+  std::function_ref<long(CountedArg) const noexcept> r6r(r1);
> >+  VERIFY( r6r(c) == 2 );
> >+  std::function_ref<long(CountedArg) const noexcept> r6m(m1);
> >+  VERIFY( r6r(c) == 2 );
> >+  std::function_ref<long(CountedArg) const noexcept> r6c(c1);
> >+  VERIFY( r6r(c) == 2 );
> > }
> >
> > void
> >@@ -110,7 +149,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 );
> >
> >@@ -119,7 +158,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 );
> > }
> >@@ -234,7 +273,7 @@ test07()
> >   std::function_ref<int()> r3r(r1);
> >   VERIFY( r3r() == 2 );
> >   r1 = f1;
> >-  VERIFY( r3r() == 1 ); // converting-constructor
> >+  VERIFY( r3r() == 2 ); // converting-constructor
> >
> >   std::function_ref<int()> r3m(m1);
> >   VERIFY( r3m() == 2 );
> >@@ -263,6 +302,19 @@ test08()
> > void
> > test09()
> > {
> >+  auto f = [](CountedArg arg) noexcept { return arg.counter; };
> >+  // For non-tirival types Param and Param&& are compatible.
> >+  std::function_ref<int(CountedArg) const noexcept> r1(f);
> >+  VERIFY( r1({}) == 1 );
> >+  std::function_ref<int(CountedArg&&) const noexcept> r2(r1);
> >+  VERIFY( r2({}) == 1 );
> >+
> >+  auto fs = [](CountedArg const& arg, std::string) noexcept { return
> arg.counter; };
> >+  std::function_ref<int(CountedArg, std::string) const noexcept> rs1(fs);
> >+  VERIFY( rs1(c, "") == 1 );
> >+  std::function_ref<int(CountedArg, std::string&&) const noexcept>
> rs2(rs1);
> >+  VERIFY( rs2(c, "") == 1 );
> >+
> >   // Scalar types and small trivially move constructible types are passed
> >   // by value to invoker. So int&& signature is not compatible for such
> types.
> >   auto fi = [](CountedArg const& arg, int) noexcept { return
> arg.counter; };
> >@@ -271,11 +323,20 @@ test09()
> >   std::function_ref<int(CountedArg, int&&) const noexcept> ri2(ri1);
> >   VERIFY( ri2(c, 0) == 2 );
> >
> >-  auto fs = [](CountedArg const& arg, std::string_view) noexcept {
> return arg.counter; };
> >-  std::function_ref<int(CountedArg, std::string_view) const noexcept>
> rs1(fs);
> >-  VERIFY( rs1(c, "") == 1 );
> >-  std::function_ref<int(CountedArg, std::string_view&&) const noexcept>
> rs2(rs1);
> >-  VERIFY( rs2(c, "") == 2 );
> >+  auto fv = [](CountedArg const& arg, std::string_view) noexcept {
> return arg.counter; };
> >+  std::function_ref<int(CountedArg, std::string_view) const noexcept>
> rv1(fv);
> >+  VERIFY( rv1(c, "") == 1 );
> >+  std::function_ref<int(CountedArg, std::string_view&&) const noexcept>
> rv2(rv1);
> >+  VERIFY( rv2(c, "") == 2 );
> >+
> >+  // CV-qual on by-value return is also ignored
> >+  auto fr = [](CountedArg const& arg) noexcept { return arg; };
> >+  std::function_ref<CountedArg(CountedArg) const noexcept> rr1(fr);
> >+  VERIFY( rr1(c).counter == 2 );
> >+  std::function_ref<const CountedArg(CountedArg) const noexcept>
> rr2(rr1);
> >+  VERIFY( rr2(c).counter == 2 );
> >+  std::function_ref<CountedArg(CountedArg)> rr3(rr2);
> >+  VERIFY( rr3(c).counter == 2 );
> > }
> >
> > int main()
> >diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> >index 4ef5d067555..4a7b102c788 100644
> >--- a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> >+++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> >@@ -1,12 +1,13 @@
> > // { dg-do compile { target c++26 } }
> >
> > #include <functional>
> >+#include <string>
> >
> >-template<typename F>
> >-constexpr std::function_ref<int(int, int) const>
> >+template<typename Signature = int(int, int) const, typename F>
> >+constexpr std::function_ref<Signature>
> > create(F f)
> > {
> >-  std::function_ref<int(int, int) const> fr(f);
> >+  std::function_ref<Signature> fr(f);
> >   return fr;
> > }
> >
> >@@ -72,3 +73,13 @@ struct StaticWinsET {
> >
> > constexpr auto vStaticWinsET = create(StaticWinsET{});
> >
> >+auto func = [](std::string s) noexcept -> int
> >+{ return s.size(); };
> >+
> >+constexpr std::function_ref<int(std::string)> stdCompatibleFRef
> >+  = std::function_ref<int(std::string) const noexcept>(func);
> >+constexpr std::function_ref<int(std::string&&)> implCompatible1FRef
> >+  = std::function_ref<int(std::string)>(func);
> >+constexpr std::function_ref<int(std::string) const> implCompatible2FRef
> >+  = std::function_ref<int(std::string)>(func);
> >+
> >diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> >index 16033014703..4e99f82a50c 100644
> >--- a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> >+++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> >@@ -74,3 +74,11 @@ struct InstanceWinsET {
> >
> > constexpr auto vInstanceWinsET = create(InstanceWinsET{}); // { dg-error
> "is not a constant expression" }
> >
> >+constexpr auto func = [](std::string s) noexcept -> int
> >+{ return s.size(); };
> >+
> >+constexpr std::function_ref<int(const std::string&)> incompatible1FRef
> >+  = std::function_ref<int(std::string)>(func); // { dg-error "is not a
> constant expression" }
> >+constexpr std::function_ref<long(std::string)> incompatible2FRef
> >+  = std::function_ref<int(std::string)>(func); // { dg-error "is not a
> constant expression" }
> >+
> >--
> >2.53.0
> >
> >
>
>

Reply via email to