[PATCH v4] libstdc++: Implement C++26 function_ref [PR119126]

Jonathan Wakely jwakely@redhat.com
Tue May 20 16:00:12 GMT 2025


On 16/05/25 15:56 +0200, Tomasz Kamiński wrote:
>This patch implements C++26 function_ref as specified in P0792R14,
>with correction for constraints for constructor accepting nontype_t
>parameter from LWG 4256.
>
>As function_ref may store a pointer to the const object, __Ptrs::_M_obj is
>changed to const void*, so again we do not cast away const from const
>objects. To help with necessary casts, a __polyfunc::__cast_to helper is
>added, that accepts reference to or target type direclty.
>
>The _Invoker now defines additional call methods used by function_ref:
>_S_ptrs() for invoking target passed by reference, and __S_nttp, _S_bind_ptr,
>_S_bind_ref for handling constructors accepting nontype_t. The existing
>_S_call_storage is changed to thin wrapper, that initialies _Ptrs, and forwards
>to _S_call_ptrs.
>
>This reduced the most uses of _Storage::_M_ptr and _Storage::_M_ref,
>so this functions was removed, and _Manager uses were adjusted.
>
>Finally we make function_ref available in freestanding mode, as
>move_only_function and copyable_function are currently only available in hosted,
>so we define _Manager and _Mo_base only if either __glibcxx_move_only_function
>or __glibcxx_copyable_function is defined.
>
>	PR libstdc++/119126
>
>libstdc++-v3/ChangeLog:
>
>	* doc/doxygen/stdheader.cc: Added funcref_impl.h file.
>	* include/Makefile.am: Added funcref_impl.h file.
>	* include/Makefile.in: Added funcref_impl.h file.
>	* include/bits/funcref_impl.h: New file.
>	* include/bits/funcwrap.h: (_Ptrs::_M_obj): Const-qualify.
>	(_Storage::_M_ptr, _Storage::_M_ref): Remove.
>	(__polyfunc::__cast_to) Define.
>	(_Base_invoker::_S_ptrs, _Base_invoker::_S_nttp)
>	(_Base_invoker::_S_bind_ptrs, _Base_invoker::_S_bind_ref)
>	(_Base_invoker::_S_call_ptrs): Define.
>	(_Base_invoker::_S_call_storage): Foward to _S_call_ptrs.
>	(_Manager::_S_local, _Manager::_S_ptr): Adjust for _M_obj being
>	const qualified.
>	(__polyfunc::_Manager, __polyfunc::_Mo_base): Guard with
>	__glibcxx_move_only_function || __glibcxx_copyable_function.
>	(__polyfunc::__skip_first_arg, __polyfunc::__deduce_funcref)
>	(std::function_ref) [__glibcxx_function_ref]: Define.
>	* include/bits/utility.h (std::nontype_t, std::nontype)
>	(__is_nontype_v) [__glibcxx_function_ref]: Define.
>	* include/bits/version.def: Define function_ref.
>	* include/bits/version.h: Regenerate.
>	* include/std/functional: Define __cpp_lib_function_ref.
>	* src/c++23/std.cc.in (std::nontype_t, std::nontype)
>	(std::function_ref) [__cpp_lib_function_ref]: Export.
>	* testsuite/20_util/function_ref/assign.cc: New test.
>	* testsuite/20_util/function_ref/call.cc: New test.
>	* testsuite/20_util/function_ref/cons.cc: New test.
>	* testsuite/20_util/function_ref/cons_neg.cc: New test.
>	* testsuite/20_util/function_ref/conv.cc: New test.
>	* testsuite/20_util/function_ref/deduction.cc: New test.
>---
>Removed unnecessary this-> qualification in function_ref.
>Use auto* to store result of __polyfunc::__cast_to.
>Added comment for about additional partial specializations for
>__skip_first_arg.
>
> libstdc++-v3/doc/doxygen/stdheader.cc         |   1 +
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/bits/funcref_impl.h      | 198 ++++++++++++++++
> libstdc++-v3/include/bits/funcwrap.h          | 188 +++++++++++----
> libstdc++-v3/include/bits/utility.h           |  17 ++
> libstdc++-v3/include/bits/version.def         |   8 +
> libstdc++-v3/include/bits/version.h           |  10 +
> libstdc++-v3/include/std/functional           |   3 +-
> libstdc++-v3/src/c++23/std.cc.in              |   7 +
> .../testsuite/20_util/function_ref/assign.cc  | 108 +++++++++
> .../testsuite/20_util/function_ref/call.cc    | 186 +++++++++++++++
> .../testsuite/20_util/function_ref/cons.cc    | 218 ++++++++++++++++++
> .../20_util/function_ref/cons_neg.cc          |  30 +++
> .../testsuite/20_util/function_ref/conv.cc    | 152 ++++++++++++
> .../20_util/function_ref/deduction.cc         | 103 +++++++++
> 16 files changed, 1184 insertions(+), 47 deletions(-)
> create mode 100644 libstdc++-v3/include/bits/funcref_impl.h
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/assign.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/call.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
>
>diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc b/libstdc++-v3/doc/doxygen/stdheader.cc
>index 839bfc81bc0..938b2b04a26 100644
>--- a/libstdc++-v3/doc/doxygen/stdheader.cc
>+++ b/libstdc++-v3/doc/doxygen/stdheader.cc
>@@ -55,6 +55,7 @@ void init_map()
>     headers["functional_hash.h"]        = "functional";
>     headers["mofunc_impl.h"]            = "functional";
>     headers["cpyfunc_impl.h"]           = "functional";
>+    headers["funcref_impl.h"]           = "functional";
>     headers["funcwrap.h"]               = "functional";
>     headers["invoke.h"]                 = "functional";
>     headers["ranges_cmp.h"]             = "functional";
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 3e5b6c4142e..baf0290d655 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -205,6 +205,7 @@ bits_headers = \
> 	${bits_srcdir}/fs_ops.h \
> 	${bits_srcdir}/fs_path.h \
> 	${bits_srcdir}/fstream.tcc \
>+	${bits_srcdir}/funcref_impl.h \
> 	${bits_srcdir}/funcwrap.h \
> 	${bits_srcdir}/gslice.h \
> 	${bits_srcdir}/gslice_array.h \
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index 3531162b5f7..e4e1079d8bd 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in
>@@ -559,6 +559,7 @@ bits_freestanding = \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/fs_path.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/fstream.tcc \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/funcwrap.h \
>+@GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/funcref_impl.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/gslice.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/gslice_array.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/hashtable.h \
>diff --git a/libstdc++-v3/include/bits/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h
>new file mode 100644
>index 00000000000..bf958aafa08
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/funcref_impl.h
>@@ -0,0 +1,198 @@
>+// Implementation of std::function_ref -*- C++ -*-
>+
>+// Copyright The GNU Toolchain Authors.
>+//
>+// This file is part of the GNU ISO C++ Library.  This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/bits/funcref_impl.h
>+ *  This is an internal header file, included by other library headers.
>+ *  Do not attempt to use it directly. @headername{functional}
>+ */
>+
>+#ifndef _GLIBCXX_MOF_CV
>+# define _GLIBCXX_MOF_CV
>+#endif
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  /// @cond undocumented
>+  namespace __polyfunc
>+  {
>+    template<bool _Noex, typename _Ret, typename _Class, typename... _Args>
>+      struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV
>+			      noexcept(_Noex)>
>+      { using type = _Ret(_Args...) noexcept(_Noex); };
>+
>+    template<bool _Noex, typename _Ret, typename _Class, typename... _Args>
>+      struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV&
>+			      noexcept(_Noex)>
>+      { using type = _Ret(_Args...) noexcept(_Noex); };
>+  } // namespace __polyfunc
>+  /// @endcond
>+
>+  /**
>+   *  @brief Non-owning polymorphic function wrapper.
>+   *  @ingroup functors
>+   *  @since C++26
>+   *  @headerfile functional
>+   *
>+   *  The `std::function_ref` class template is a non-owning call wrapper,
>+   *  that refers a bound object. Using function_ref outside of lifetime

"... refers to a bound object."
"outside of the lifetime of the bound object ..."

>+   *  of bound object has undefined behavior.
>+   *
>+   *  It supports const-qualification and no-throw guarantees. The qualifications and
>+   *  exception-specification of the signature are respected when when invoking the

s/when when/when/

Also please wrap these comments to break before 80 cols.

>+   *  reference function.
>+   */
>+  template<typename _Res, typename... _ArgTypes, bool _Noex>
>+    class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
>+		       noexcept(_Noex)>
>+    {
>+      using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
>+      using _Signature = _Invoker::_Signature;
>+
>+      // [func.wrap.ref.ctor]/1 is-invokable-using
>+      template<typename... _Tps>
>+	static constexpr bool __is_invocable_using
>+	  = __conditional_t<_Noex,
>+			    is_nothrow_invocable_r<_Res, _Tps..., _ArgTypes...>,
>+			    is_invocable_r<_Res, _Tps..., _ArgTypes...>>::value;
>+
>+    public:
>+      /// Target and bound object is function pointed by parameter.
>+      template<typename _Fn>
>+	requires is_function_v<_Fn> && __is_invocable_using<_Fn*>
>+	function_ref(_Fn* __fn) noexcept
>+	{
>+	  __glibcxx_assert(__fn != nullptr);
>+	  _M_invoke = _Invoker::template _S_ptrs<_Fn*>();
>+	  _M_init(__fn);
>+	}
>+
>+      /// Target and bound object is object referenced by parameter.
>+      template<typename _Fn, typename _Vt = remove_reference_t<_Fn>>
>+	requires (!is_same_v<remove_cv_t<_Vt>, function_ref>)
>+	       && (!is_member_pointer_v<_Vt>)
>+	       // We deviate from standard by having this condition, that forces
>+	       // function references to use _Fn* constructors. This simplies
>+	       // implementation and provide better diagnostic when used in
>+	       // constant expression (above constructor is not constexpr).
>+	       && (!is_function_v<_Vt>)
>+	       && __is_invocable_using<_Vt _GLIBCXX_MOF_CV&>
>+	constexpr
>+	function_ref(_Fn&& __f) noexcept
>+	{
>+	  _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
>+	  _M_init(std::addressof(__f));
>+	}
>+
>+      // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+      // 4256. Incorrect constrains for function_ref constructors from nontype
>+      /// Target object is __fn. There is no bound object.
>+      template<auto __fn>
>+	requires __is_invocable_using<const decltype(__fn)&>
>+	constexpr
>+	function_ref(nontype_t<__fn>) noexcept
>+	{
>+	  using _Fn = remove_cv_t<decltype(__fn)>;
>+	  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;
>+	}
>+
>+      /// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)).
>+      /// Bound object is object referenced by second parameter.
>+      template<auto __fn, typename _Up, typename _Td = remove_reference_t<_Up>>
>+	requires (!is_rvalue_reference_v<_Up&&>)
>+	  && __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV&>
>+	constexpr
>+	function_ref(nontype_t<__fn>, _Up&& __ref) noexcept
>+	{
>+	  using _Fn = remove_cv_t<decltype(__fn)>;
>+	  if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
>+	    static_assert(__fn != nullptr);
>+
>+	  using _Tr = _Td _GLIBCXX_MOF_CV&;
>+	  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).
>+      /// Bound object is object pointed by second parameter (if any).
>+      template<auto __fn, typename _Td>
>+	requires __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV*>
>+	constexpr
>+	function_ref(nontype_t<__fn>, _Td _GLIBCXX_MOF_CV* __ptr) noexcept
>+	{
>+	  using _Fn = remove_cv_t<decltype(__fn)>;
>+	  if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
>+	    static_assert(__fn != nullptr);
>+	  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);
>+	}
>+
>+      template<typename _Tp>
>+	requires (!is_same_v<_Tp, function_ref>)
>+	       && (!is_pointer_v<_Tp>) && (!__is_nontype_v<_Tp>)
>+	function_ref&
>+	operator=(_Tp) = delete;
>+
>+      /** Invoke the target object.
>+       *
>+       * The bound object will be invoked using the supplied arguments,
>+       * and as const or non-const, as dictated by the template arguments
>+       * of the `function_ref` specialization.
>+       */
>+      _Res
>+      operator()(_ArgTypes... __args) const noexcept(_Noex)
>+      { 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);

Does this mean we can't construct a function_ref from nontype<func> in
constant evaluation? Is that OK?

>+	  else
>+	    _M_ptrs._M_obj = __ptr;
>+	}
>+
>+      typename _Invoker::__ptrs_func_t _M_invoke;
>+      __polyfunc::_Ptrs _M_ptrs;
>+    };
>+
>+#undef _GLIBCXX_MOF_CV
>+
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>diff --git a/libstdc++-v3/include/bits/funcwrap.h b/libstdc++-v3/include/bits/funcwrap.h
>index 4e053534cf5..cf261bcd4c8 100644
>--- a/libstdc++-v3/include/bits/funcwrap.h
>+++ b/libstdc++-v3/include/bits/funcwrap.h
>@@ -1,4 +1,5 @@
>-// Implementation of std::move_only_function and std::copyable_function -*- C++ -*-
>+// Implementation of std::move_only_function, std::copyable_function
>+// and std::function_ref  -*- C++ -*-
>
> // Copyright The GNU Toolchain Authors.
> //
>@@ -36,7 +37,7 @@
>
> #include <bits/version.h>
>
>-#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function)
>+#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref
>
> #include <bits/invoke.h>
> #include <bits/utility.h>
>@@ -53,10 +54,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   {
>     union _Ptrs
>     {
>-      void* _M_obj;
>+      const void* _M_obj;
>       void (*_M_func)();
>     };
>
>+   template<typename _Tp>
>+     [[__gnu__::__always_inline__]]
>+     constexpr auto*
>+     __cast_to(_Ptrs __ptrs) noexcept
>+     {
>+       using _Td = remove_reference_t<_Tp>;
>+       if constexpr (is_function_v<_Td>)
>+	 return reinterpret_cast<_Td*>(__ptrs._M_func);
>+       else if constexpr (is_const_v<_Td>)
>+	 return static_cast<_Td*>(__ptrs._M_obj);
>+       else
>+	 return static_cast<_Td*>(const_cast<void*>(__ptrs._M_obj));
>+     }
>+
>    struct _Storage
>    {
>      void*       _M_addr() noexcept       { return &_M_bytes[0]; }
>@@ -97,32 +112,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	   ::new (_M_addr()) _Tp(std::forward<_Args>(__args)...);
>        }
>
>-     template<typename _Tp>
>-       [[__gnu__::__always_inline__]]
>-       _Tp*
>-       _M_ptr() const noexcept
>-       {
>-	 if constexpr (!_S_stored_locally<remove_const_t<_Tp>>())
>-	   return static_cast<_Tp*>(_M_ptrs._M_obj);
>-	 else if constexpr (is_const_v<_Tp>)
>-	   return static_cast<_Tp*>(_M_addr());
>-	 else
>-	   // _Manager and _Invoker pass _Storage by const&, even for mutable sources.
>-	   return static_cast<_Tp*>(const_cast<void*>(_M_addr()));
>-       }
>-
>-     template<typename _Ref>
>-       [[__gnu__::__always_inline__]]
>-       _Ref
>-       _M_ref() const noexcept
>-       {
>-	 using _Tp = remove_reference_t<_Ref>;
>-	 if constexpr (is_function_v<remove_pointer_t<_Tp>>)
>-	   return reinterpret_cast<_Tp>(_M_ptrs._M_func);
>-	 else
>-	   return static_cast<_Ref>(*_M_ptr<_Tp>());
>-       }
>-
>      // We want to have enough space to store a simple delegate type.
>      struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; };
>      union {
>@@ -143,6 +132,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	 _S_storage()
> 	 { return &_S_call_storage<_Adjust_target<_Tp>>; }
>
>+       using __ptrs_func_t = _Ret(*)(_Ptrs, _Args...) noexcept(_Noex);
>+       template<typename _Tp>
>+	 static consteval __ptrs_func_t
>+	 _S_ptrs()
>+	 { return &_S_call_ptrs<_Adjust_target<_Tp>>; }
>+
>+#ifdef __glibcxx_function_ref // C++ >= 26
>+       template<auto __fn>
>+	 static _Ret
>+	 _S_nttp(_Ptrs, _Args... __args) noexcept(_Noex)
>+	 { return std::__invoke_r<_Ret>(__fn, std::forward<_Args>(__args)...); }
>+
>+       template<auto __fn, typename _Tp>
>+	 static _Ret
>+	 _S_bind_ptr(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
>+	 {
>+	   auto* __p = __polyfunc::__cast_to<_Tp>(__ptrs);
>+	   return std::__invoke_r<_Ret>(__fn, __p,
>+					std::forward<_Args>(__args)...);
>+	 }
>+
>+       template<auto __fn, typename _Ref>
>+	 static _Ret
>+	 _S_bind_ref(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
>+	 {
>+	   auto* __p = __polyfunc::__cast_to<_Ref>(__ptrs);
>+	   return std::__invoke_r<_Ret>(__fn, static_cast<_Ref>(*__p),
>+					std::forward<_Args>(__args)...);
>+	 }
>+#endif // __glibcxx_function_ref
>+
>      private:
>        template<typename _Tp, typename _Td = remove_cvref_t<_Tp>>
> 	 using _Adjust_target =
>@@ -152,8 +172,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	 static _Ret
> 	 _S_call_storage(const _Storage& __ref, _Args... __args) noexcept(_Noex)
> 	 {
>-	   return std::__invoke_r<_Ret>(__ref._M_ref<_Tp>(),
>-					std::forward<_Args>(__args)...);
>+	   _Ptrs __ptrs;
>+	   if constexpr (is_function_v<remove_pointer_t<_Tp>>)
>+	     __ptrs._M_func = __ref._M_ptrs._M_func;
>+	   else if constexpr (!_Storage::_S_stored_locally<remove_cvref_t<_Tp>>())
>+	     __ptrs._M_obj = __ref._M_ptrs._M_obj;
>+	   else
>+	     __ptrs._M_obj = __ref._M_addr();
>+	   return _S_call_ptrs<_Tp>(__ptrs, std::forward<_Args>(__args)...);
>+	 }
>+
>+       template<typename _Tp>
>+	 static _Ret
>+	 _S_call_ptrs(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
>+	 {
>+	   if constexpr (is_function_v<remove_pointer_t<_Tp>>)
>+	     return std::__invoke_r<_Ret>(reinterpret_cast<_Tp>(__ptrs._M_func),
>+					  std::forward<_Args>(__args)...);
>+	   else
>+	     {
>+	       auto* __p = __polyfunc::__cast_to<_Tp>(__ptrs);
>+	       return std::__invoke_r<_Ret>(static_cast<_Tp>(*__p),
>+					    std::forward<_Args>(__args)...);
>+	     }
> 	 }
>      };
>
>@@ -184,6 +225,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	 return false;
>      }
>
>+#if __glibcxx_move_only_function || __glibcxx_copyable_function
>    struct _Manager
>    {
>      enum class _Op
>@@ -241,7 +283,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        switch (__op)
>        {
> 	 case _Op::_Address:
>-	   __target._M_ptrs._M_obj = const_cast<void*>(__src->_M_addr());
>+	   __target._M_ptrs._M_obj = __src->_M_addr();
> 	   return;
> 	 case _Op::_Move:
> 	 case _Op::_Copy:
>@@ -263,24 +305,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	 switch (__op)
> 	 {
> 	   case _Op::_Address:
>-	     __target._M_ptrs._M_obj = __src->_M_ptr<_Tp>();
>+	     __target._M_ptrs._M_obj = __src->_M_addr();
> 	     return;
> 	   case _Op::_Move:
> 	     {
>-	       _Tp* __obj = __src->_M_ptr<_Tp>();
>+	       _Tp* __obj = static_cast<_Tp*>(const_cast<void*>(__src->_M_addr()));
> 	       ::new(__target._M_addr()) _Tp(std::move(*__obj));
> 	       __obj->~_Tp();
> 	     }
> 	     return;
> 	   case _Op::_Destroy:
>-	     __target._M_ptr<_Tp>()->~_Tp();
>+	     static_cast<_Tp*>(__target._M_addr())->~_Tp();
> 	     return;
> 	   case _Op::_Copy:
> 	     if constexpr (_Provide_copy)
>-	       ::new (__target._M_addr()) _Tp(__src->_M_ref<const _Tp&>());
>-	     else
>-	       __builtin_unreachable();
>-	     return;
>+	       {
>+		 auto* __obj = static_cast<const _Tp*>(__src->_M_addr());
>+		 ::new (__target._M_addr()) _Tp(*__obj);
>+		 return;
>+	       }
>+	     __builtin_unreachable();
> 	 }
>        }
>
>@@ -296,14 +340,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	     __target._M_ptrs._M_obj = __src->_M_ptrs._M_obj;
> 	     return;
> 	   case _Op::_Destroy:
>-	     delete __target._M_ptr<_Tp>();
>+	     delete static_cast<const _Tp*>(__target._M_ptrs._M_obj);
> 	     return;
> 	   case _Op::_Copy:
> 	     if constexpr (_Provide_copy)
>-	       __target._M_ptrs._M_obj = new _Tp(__src->_M_ref<const _Tp&>());
>-	     else
>-	       __builtin_unreachable();
>-	     return;
>+	       {
>+		 auto* __obj = static_cast<const _Tp*>(__src->_M_ptrs._M_obj);
>+		 __target._M_ptrs._M_obj = new _Tp(*__obj);
>+		 return;
>+	       }
>+	     __builtin_unreachable();
> 	  }
> 	}
>    };
>@@ -382,7 +428,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      friend class _Cpy_base;
> #endif // __glibcxx_copyable_function
>    };
>-
>+#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
> } // namespace __polyfunc
>   /// @endcond
>
>@@ -468,6 +514,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   }  // namespace __detail::__variant
> #endif // __glibcxx_copyable_function
>
>+#ifdef __glibcxx_function_ref  // C++ >= 26
>+  /// @cond undocumented
>+  namespace __polyfunc
>+  {
>+    template<typename _Sig>
>+      struct __skip_first_arg;
>+
>+    // Additional partial specializations are defined in bits/funcref_impl.h
>+    template<bool _Noex, typename _Ret, typename _Arg, typename... _Args>
>+      struct __skip_first_arg<_Ret(*)(_Arg, _Args...) noexcept(_Noex)>
>+      { using type = _Ret(_Args...) noexcept(_Noex); };
>+
>+    template<typename _Fn, typename _Tr>
>+      consteval auto
>+      __deduce_funcref()
>+      {
>+	if constexpr (is_member_object_pointer_v<_Fn>)
>+	  // TODO Consider reporting issue to make this noexcept
>+	  return static_cast<invoke_result_t<_Fn, _Tr>(*)()>(nullptr);
>+	else
>+	  return static_cast<__skip_first_arg<_Fn>::type*>(nullptr);
>+      }
>+  } // namespace __polyfunc
>+  /// @endcond
>+
>+  template<typename... _Signature>
>+    class function_ref; // not defined
>+
>+  template<typename _Fn>
>+    requires is_function_v<_Fn>
>+    function_ref(_Fn*) -> function_ref<_Fn>;
>+
>+  template<auto __f, class _Fn = remove_pointer_t<decltype(__f)>>
>+    requires is_function_v<_Fn>
>+    function_ref(nontype_t<__f>) -> function_ref<_Fn>;
>+
>+  template<auto __f, typename _Tp, class _Fn = decltype(__f)>
>+    requires is_member_pointer_v<_Fn> || is_function_v<remove_pointer_t<_Fn>>
>+    function_ref(nontype_t<__f>, _Tp&&)
>+      -> function_ref<
>+	   remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn, _Tp&>())>>;
>+
>+#endif // __glibcxx_function_ref
>+
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
>
>@@ -503,5 +593,11 @@ _GLIBCXX_END_NAMESPACE_VERSION
> #include "cpyfunc_impl.h"
> #endif // __glibcxx_copyable_function
>
>-#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
>+#ifdef __glibcxx_function_ref  // C++ >= 26
>+#include "funcref_impl.h"
>+#define _GLIBCXX_MOF_CV const
>+#include "funcref_impl.h"
>+#endif // __glibcxx_function_ref
>+
>+#endif // move_only_function || copyable_function || function_ref
> #endif // _GLIBCXX_FUNCWRAP_H
>diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h
>index 6fa6b679c6d..84d25e0c658 100644
>--- a/libstdc++-v3/include/bits/utility.h
>+++ b/libstdc++-v3/include/bits/utility.h
>@@ -316,6 +316,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   inline constexpr sorted_equivalent_t sorted_equivalent{};
> #endif
>
>+#if __glibcxx_function_ref // >= C++26
>+  template<auto>
>+    struct nontype_t
>+    {
>+      explicit nontype_t() = default;
>+    };
>+
>+  template<auto __val>
>+    constexpr nontype_t<__val> nontype{};
>+
>+  template<typename>
>+    inline constexpr bool __is_nontype_v = false;
>+
>+  template<auto __val>
>+    inline constexpr bool __is_nontype_v<nontype_t<__val>> = true;
>+#endif
>+
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace
>
>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
>index 6ca148f0488..55aab40d9bf 100644
>--- a/libstdc++-v3/include/bits/version.def
>+++ b/libstdc++-v3/include/bits/version.def
>@@ -1756,6 +1756,14 @@ ftms = {
>   };
> };
>
>+ftms = {
>+  name = function_ref;
>+  values = {
>+    v = 202306;
>+    cxxmin = 26;
>+  };
>+};
>+
> ftms = {
>   name = out_ptr;
>   values = {
>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
>index 48a090c14a3..026c9959482 100644
>--- a/libstdc++-v3/include/bits/version.h
>+++ b/libstdc++-v3/include/bits/version.h
>@@ -1958,6 +1958,16 @@
> #endif /* !defined(__cpp_lib_copyable_function) && defined(__glibcxx_want_copyable_function) */
> #undef __glibcxx_want_copyable_function
>
>+#if !defined(__cpp_lib_function_ref)
>+# if (__cplusplus >  202302L)
>+#  define __glibcxx_function_ref 202306L
>+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_function_ref)
>+#   define __cpp_lib_function_ref 202306L
>+#  endif
>+# endif
>+#endif /* !defined(__cpp_lib_function_ref) && defined(__glibcxx_want_function_ref) */
>+#undef __glibcxx_want_function_ref
>+
> #if !defined(__cpp_lib_out_ptr)
> # if (__cplusplus >= 202100L)
> #  define __glibcxx_out_ptr 202311L
>diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
>index 9a55b181e6d..307bcb95bcc 100644
>--- a/libstdc++-v3/include/std/functional
>+++ b/libstdc++-v3/include/std/functional
>@@ -57,6 +57,7 @@
> #define __glibcxx_want_bind_back
> #define __glibcxx_want_constexpr_functional
> #define __glibcxx_want_copyable_function
>+#define __glibcxx_want_function_ref
> #define __glibcxx_want_invoke
> #define __glibcxx_want_invoke_r
> #define __glibcxx_want_move_only_function
>@@ -86,7 +87,7 @@
> # include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc.
> # include <compare>
> #endif
>-#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function)
>+#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref
> # include <bits/funcwrap.h>
> #endif
>
>diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
>index 417c8a1a562..ba468530338 100644
>--- a/libstdc++-v3/src/c++23/std.cc.in
>+++ b/libstdc++-v3/src/c++23/std.cc.in
>@@ -1414,6 +1414,9 @@ export namespace std
> #endif
> #if __cpp_lib_copyable_function
>   using std::copyable_function;
>+#endif
>+#if __cpp_lib_function_ref
>+  using std::function_ref;
> #endif
>   using std::multiplies;
>   using std::negate;
>@@ -3195,6 +3198,10 @@ export namespace std
>   using std::make_integer_sequence;
>   using std::move;
>   using std::move_if_noexcept;
>+#if __cpp_lib_function_ref
>+  using std::nontype_t;
>+  using std::nontype;
>+#endif
>   using std::pair;
>   using std::swap;
>   using std::operator==;
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
>new file mode 100644
>index 00000000000..9b02dc49c2a
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
>@@ -0,0 +1,108 @@
>+// { dg-do compile { target c++26 } }
>+
>+#include <functional>
>+
>+#ifndef __cpp_lib_function_ref
>+# error "Feature-test macro for function_ref missing in <functional>"
>+#elif __cpp_lib_function_ref != 202306L
>+# error "Feature-test macro for function_ref has wrong value in <functional>"
>+#endif
>+
>+using std::nontype;
>+using std::nontype_t;
>+using std::function_ref;
>+
>+using std::is_nothrow_move_assignable_v;
>+using std::is_nothrow_copy_assignable_v;
>+using std::is_nothrow_assignable_v;
>+using std::is_assignable_v;
>+using std::is_nothrow_swappable_v;
>+using std::is_trivially_copyable_v;
>+
>+static_assert( is_nothrow_move_assignable_v<function_ref<void()>> );
>+static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> );
>+static_assert( is_nothrow_swappable_v<function_ref<void()>> );
>+
>+static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t> );
>+
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> );
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()> );
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()> );
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> );
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()> );
>+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()> );
>+static_assert( ! is_nothrow_assignable_v<function_ref<void()>, void(int)> );
>+static_assert( is_nothrow_assignable_v<function_ref<void(int)>, void(int)> );
>+
>+static_assert( is_nothrow_assignable_v<function_ref<void()>,
>+				       void() noexcept> );
>+static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>,
>+					void() noexcept> );
>+static_assert( ! is_assignable_v<function_ref<void() noexcept>, void() > );
>+
>+struct S
>+{
>+  int x;
>+  int f();
>+};
>+int funS(S);
>+
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       decltype(funS)> );
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       decltype(&funS)> );
>+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)> );
>+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)> );
>+
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       nontype_t<funS>> );
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       nontype_t<&funS>> );
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       nontype_t<&S::x>> );
>+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
>+				       nontype_t<&S::f>> );
>+struct Q
>+{
>+  void operator()() const;
>+};
>+
>+static_assert( ! is_assignable_v<function_ref<void()>, Q> );
>+static_assert( ! is_assignable_v<function_ref<void()>, Q&> );
>+static_assert( ! is_assignable_v<function_ref<void()>, const Q&> );
>+static_assert( ! is_assignable_v<function_ref<void() const>, Q> );
>+static_assert( ! is_assignable_v<function_ref<void() const>, Q&> );
>+static_assert( ! is_assignable_v<function_ref<void() const>, const Q&> );
>+
>+static_assert( is_nothrow_assignable_v<function_ref<void()>,
>+				       nontype_t<Q{}>> );
>+static_assert( is_nothrow_assignable_v<function_ref<void() const>,
>+				       nontype_t<Q{}>> );
>+
>+constexpr bool
>+test_constexpr()
>+{
>+  function_ref<void(S)> fp(nontype<funS>);
>+  fp = nontype<funS>;
>+  fp = nontype<&funS>;
>+  fp = nontype<&S::x>;
>+  fp = nontype<&S::f>;
>+
>+  constexpr Q cq;
>+  function_ref<void() const> fq(cq);
>+  fq = nontype<cq>;
>+  return true;
>+}
>+static_assert( test_constexpr() );
>+
>+void func();
>+
>+void
>+test_instantiation()
>+{
>+  function_ref<void(S)> fp(funS);
>+  fp = funS;
>+  fp = &funS;
>+
>+  test_constexpr();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
>new file mode 100644
>index 00000000000..a91c6b4abb9
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
>@@ -0,0 +1,186 @@
>+// { dg-do run { target c++26 } }
>+
>+#include <functional>
>+#include <utility>
>+#include <testsuite_hooks.h>
>+
>+using std::nontype;
>+using std::function_ref;
>+
>+using std::is_same_v;
>+using std::is_invocable_v;
>+using std::is_nothrow_invocable_v;
>+using std::invoke_result_t;
>+
>+// Check return types
>+static_assert( is_same_v<void, invoke_result_t<function_ref<void()>>> );
>+static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> );
>+static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> );
>+
>+// Const qualier applies to target object
>+static_assert( is_invocable_v< function_ref<void()> const > );
>+static_assert( is_invocable_v< function_ref<void()> const &> );
>+static_assert( is_invocable_v< function_ref<void() const> > );
>+static_assert( is_invocable_v< function_ref<void() const> &> );
>+static_assert( is_invocable_v< function_ref<void() const> const > );
>+static_assert( is_invocable_v< function_ref<void() const> const &> );
>+
>+// With noexcept-specifier
>+static_assert( ! is_nothrow_invocable_v< function_ref<void()> > );
>+static_assert( ! is_nothrow_invocable_v< function_ref<void() noexcept(false)> > );
>+static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> > );
>+
>+void
>+test01()
>+{
>+  struct F
>+  {
>+    int operator()() { return 0; }
>+    int operator()() const { return 1; }
>+  };
>+
>+  function_ref<int()> f0{F{}};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  function_ref<int()> f1{nontype<F{}>};
>+  VERIFY( f1() == 1 );
>+  VERIFY( std::move(f1)() == 1 );
>+
>+  function_ref<int() const> f2{F{}};
>+  VERIFY( f2() == 1 );
>+  VERIFY( std::as_const(f2)() == 1 );
>+  VERIFY( std::move(f2)() == 1 );
>+  VERIFY( std::move(std::as_const(f2))() == 1 );
>+
>+  function_ref<int() const> f3{nontype<F{}>};
>+  VERIFY( f3() == 1 );
>+  VERIFY( std::as_const(f3)() == 1 );
>+  VERIFY( std::move(f3)() == 1 );
>+  VERIFY( std::move(std::as_const(f3))() == 1 );
>+}
>+
>+void
>+test02()
>+{
>+  struct F
>+  {
>+    struct Arg {};
>+    int operator()(Arg& arg) const { return 0; }
>+    int operator()(const Arg& arg) const { return 1; }
>+  };
>+  F::Arg arg;
>+
>+  function_ref<int()> f0{std::nontype<F{}>, arg};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  function_ref<int() const> f1{std::nontype<F{}>, arg};
>+  VERIFY( f1() == 1 );
>+  VERIFY( std::as_const(f1)() == 1 );
>+}
>+
>+void
>+test03()
>+{
>+  struct F
>+  {
>+    struct Arg {};
>+    int operator()(Arg* arg) const { return 0; }
>+    int operator()(const Arg* arg) const { return 1; }
>+  };
>+  F::Arg arg;
>+
>+  function_ref<int()> f0{std::nontype<F{}>, &arg};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  function_ref<int() const> f1{std::nontype<F{}>, &arg};
>+  VERIFY( f1() == 1 );
>+  VERIFY( std::as_const(f1)() == 1 );
>+}
>+
>+void
>+test04()
>+{
>+  constexpr int (*fp)() = [] { return 0; };
>+  function_ref<int()> f0{fp};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  function_ref<int()> f1{nontype<fp>};
>+  VERIFY( f1() == 0 );
>+  VERIFY( std::move(f1)() == 0 );
>+
>+  const function_ref<int() const> f2{fp};
>+  VERIFY( f2() == 0 );
>+  VERIFY( std::move(f2)() == 0 );
>+
>+  const function_ref<int() const> f3{nontype<fp>};
>+  VERIFY( f2() == 0 );
>+  VERIFY( std::move(f2)() == 0 );
>+}
>+
>+using ftype = int(int);
>+int twice(int x) { return x * 2; }
>+int cube(int x) { return x * x * x; }
>+int callback_ptr(ftype* f, int x) { return f(x); }
>+int callback_ref(ftype& f, int x) { return f(x); }
>+
>+void
>+test05()
>+{
>+
>+  function_ref<int(int)> r1(nontype<&callback_ptr>, &twice);
>+  VERIFY( r1(2) == 4 );
>+  function_ref<int(int)> r2(nontype<&callback_ptr>, cube);
>+  VERIFY( r2(2) == 8 );
>+
>+  function_ref<int(int)> r3(nontype<&callback_ref>, twice);
>+  VERIFY( r3(3) == 6 );
>+  function_ref<int(int)> r4(nontype<&callback_ref>, cube);
>+  VERIFY( r4(3) == 27 );
>+
>+  // Checks if distinction between reference and pointer
>+  // is preserved.
>+  struct F
>+  {
>+    static
>+    int operator()(ftype* f, int x)
>+    { return f(x) + 1000; }
>+
>+    static
>+    int operator()(ftype& f, int x)
>+    { return f(x) + 2000; }
>+  };
>+  function_ref<int(int)> r5(nontype<F{}>, &twice);
>+  VERIFY( r5(2) == 1004 );
>+  function_ref<int(int)> r6(nontype<F{}>, twice);
>+  VERIFY( r6(2) == 2008 );
>+  function_ref<int(int)> r7(nontype<F{}>, &cube);
>+  VERIFY( r7(3) == 1006 );
>+  function_ref<int(int)> r8(nontype<F{}>, cube);
>+  VERIFY( r8(3) == 2027 );
>+}
>+
>+struct Incomplete;
>+
>+void
>+test_params()
>+{
>+  auto f = [](auto&&) {};
>+  // There is discussion if this is supported.
>+  // std::function_ref<void(Incomplete)> f1(f);
>+  std::function_ref<void(Incomplete&)> f2(f);
>+  // See PR120259, this should be supported.
>+  // std::function_ref<void(Incomplete&&)> f3(f);
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+  test04();
>+  test_params();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
>new file mode 100644
>index 00000000000..a91f5ba3dab
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
>@@ -0,0 +1,218 @@
>+// { dg-do compile { target c++26 } }
>+// { dg-add-options no_pch }
>+
>+#include <functional>
>+
>+#ifndef __cpp_lib_function_ref
>+# error "Feature-test macro for function_ref missing in <functional>"
>+#elif __cpp_lib_function_ref != 202306L
>+# error "Feature-test macro for function_ref has wrong value in <functional>"
>+#endif
>+
>+using std::nontype;
>+using std::nontype_t;
>+using std::function_ref;
>+
>+using std::is_default_constructible_v;
>+using std::is_nothrow_copy_constructible_v;
>+using std::is_nothrow_move_constructible_v;
>+using std::is_nothrow_constructible_v;
>+using std::is_constructible_v;
>+using std::is_trivially_copyable_v;
>+
>+static_assert( ! is_default_constructible_v<function_ref<void()>> );
>+static_assert( is_nothrow_move_constructible_v<function_ref<void()>> );
>+static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> );
>+static_assert( is_trivially_copyable_v<function_ref<void()>> );
>+
>+static_assert( ! is_constructible_v<function_ref<void()>, std::nullptr_t> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, void()> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(&)()> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(*)()> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, int()> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(&)()> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(*)()> );
>+static_assert( ! is_constructible_v<function_ref<void()>, void(int)> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, void(int)> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void()>,
>+					  void() noexcept> );
>+static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>,
>+					  void() noexcept> );
>+static_assert( ! is_constructible_v<function_ref<void() noexcept>,
>+				    void() > );
>+
>+struct S
>+{
>+  int x;
>+  int f();
>+};
>+int funS(S);
>+
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  decltype(funS)> );
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  decltype(&funS)> );
>+static_assert( ! is_constructible_v<function_ref<int(S)>,
>+				    decltype(&S::x)> );
>+static_assert( ! is_constructible_v<function_ref<int(S)>,
>+				    decltype(&S::f)> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  nontype_t<funS>> );
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  nontype_t<&funS>> );
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  nontype_t<&S::x>> );
>+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
>+					  nontype_t<&S::f>> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<funS>, S&> );
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<&funS>, S&> );
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<&S::x>, S&> );
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<&S::f>, S&> );
>+
>+static_assert( ! is_constructible_v<function_ref<int()>,
>+				    nontype_t<funS>, S*> );
>+static_assert( ! is_constructible_v<function_ref<int()>,
>+				    nontype_t<&funS>, S*> );
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<&S::x>, S*> );
>+static_assert( is_nothrow_constructible_v<function_ref<int()>,
>+					  nontype_t<&S::f>, S*> );
>+
>+struct M
>+{
>+  void operator()();
>+};
>+
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, M> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> );
>+static_assert( ! is_constructible_v<function_ref<void()>, const M&> );
>+static_assert( ! is_constructible_v<function_ref<void() const>, M> );
>+static_assert( ! is_constructible_v<function_ref<void() const>, const M&> );
>+static_assert( ! is_constructible_v<function_ref<void()>,
>+				    nontype_t<M{}>> );
>+static_assert( ! is_constructible_v<function_ref<void() const>,
>+				    nontype_t<M{}>> );
>+struct Q
>+{
>+  void operator()(int) const;
>+  void operator()(int*) const;
>+};
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, const Q&> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q&> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, const Q&> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
>+					  nontype_t<Q{}>> );
>+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>,
>+					  nontype_t<Q{}>> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>,
>+					  nontype_t<Q{}>, int&> );
>+static_assert( is_nothrow_constructible_v<function_ref<void() const>,
>+					  nontype_t<Q{}>, int&> );
>+static_assert( ! is_constructible_v<function_ref<void()>,
>+				    nontype_t<Q{}>, int> );
>+static_assert( ! is_constructible_v<function_ref<void() const>,
>+				    nontype_t<Q{}>, int> );
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void()>,
>+					  nontype_t<Q{}>, int*> );
>+static_assert( ! is_constructible_v<function_ref<void() const>,
>+				    nontype_t<Q{}>, int*> );
>+
>+struct L
>+{
>+  void operator()() &;
>+};
>+
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, L> );
>+static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> );
>+static_assert( ! is_constructible_v<function_ref<void()>, const L&> );
>+static_assert( ! is_constructible_v<function_ref<void() const>, L> );
>+static_assert( ! is_constructible_v<function_ref<void() const>, const L&> );
>+static_assert( ! is_constructible_v<function_ref<void()>,
>+				    nontype_t<L{}>> );
>+static_assert( ! is_constructible_v<function_ref<void() const>,
>+				    nontype_t<L{}>> );
>+
>+struct R
>+{
>+  void operator()(float) const&&;
>+};
>+
>+static_assert( ! is_constructible_v<function_ref<void(float)>, R> );
>+static_assert( ! is_constructible_v<function_ref<void(float)>, R&> );
>+static_assert( ! is_constructible_v<function_ref<void(float) const>, R> );
>+static_assert( ! is_constructible_v<function_ref<void(float) const>, R&> );
>+static_assert( ! is_constructible_v<function_ref<void(float) const>, const R&> );
>+
>+static_assert( ! is_constructible_v<function_ref<void(float)>,
>+						 nontype_t<R{}>> );
>+static_assert( ! is_constructible_v<function_ref<void(float) const>,
>+						 nontype_t<R{}>> );
>+
>+constexpr bool
>+test_constexpr()
>+{
>+  function_ref<void(S)> fp1(nontype<funS>);
>+  function_ref<void(S)> fp3(nontype<&funS>);
>+  function_ref<void(S)> fp4(nontype<&S::x>);
>+  function_ref<void(S)> fp5(nontype<&S::f>);
>+
>+  S s;
>+  function_ref<void()> fp6(nontype<&funS>, s);
>+  function_ref<void()> fp7(nontype<&S::x>, s);
>+  function_ref<void()> fp8(nontype<&S::x>, &s);
>+  function_ref<void()> fp9(nontype<&S::f>, s);
>+  function_ref<void()> fp10(nontype<&S::f>, &s);
>+
>+  M m;
>+  function_ref<void()> fm1(m);
>+  function_ref<void()> fm2(std::move(m));
>+
>+  Q q;
>+  constexpr Q cq;
>+  function_ref<void(int)> fq1(q);
>+  function_ref<void(int) const> fq2(q);
>+  function_ref<void(int) const> fq3(std::move(q));
>+
>+  function_ref<void(int)> fcq1(cq);
>+  function_ref<void(int) const> f(cq);
>+  function_ref<void(int)> fcq3(nontype<cq>);
>+  function_ref<void(int) const> fcq4(nontype<cq>);
>+
>+  int i = 0;
>+  function_ref<void()> fcq5(nontype<cq>, i);
>+  function_ref<void() const> fcq6(nontype<cq>, i);
>+  function_ref<void()> fcq7(nontype<cq>, &i);
>+
>+  L l;
>+  function_ref<void()> fl1(l);
>+  function_ref<void()> fl2(std::move(l));
>+
>+  return true;
>+}
>+static_assert( test_constexpr() );
>+
>+void func();
>+
>+void
>+test_instantiation()
>+{
>+  function_ref<void(S)> fp1(funS);
>+  function_ref<void(S)> fp2(&funS);
>+
>+  test_constexpr();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
>new file mode 100644
>index 00000000000..050090df370
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
>@@ -0,0 +1,30 @@
>+// { dg-do compile { target c++26 } }
>+
>+#include <functional>
>+
>+using std::nontype;
>+using std::function_ref;
>+
>+struct S
>+{
>+  int x;
>+  void foo();
>+};
>+S s;
>+
>+constexpr int(*fp)(S) = nullptr;
>+constexpr int S::*mdp = nullptr;
>+constexpr int (S::*mfp)() = nullptr;
>+
>+function_ref<int(S)> fd1(nontype<fp>);  // { dg-error "from here" }
>+function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" }
>+function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" }
>+
>+function_ref<int()> br4(nontype<fp>, s);  // { dg-error "from here" }
>+function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" }
>+function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" }
>+
>+function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" }
>+function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" }
>+
>+// { dg-prune-output "static assertion failed" }
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
>new file mode 100644
>index 00000000000..49ff0a78829
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
>@@ -0,0 +1,152 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-effective-target hosted }
>+
>+#include <functional>
>+#include <testsuite_hooks.h>
>+
>+using std::function_ref;
>+
>+static_assert( std::is_constructible_v<std::function_ref<void() const>,
>+				       std::function_ref<void()>> );
>+
>+// Non-trivial args, guarantess that type is not passed by copy
>+struct CountedArg
>+{
>+  CountedArg() = default;
>+  CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; }
>+  CountedArg& operator=(CountedArg&&) = delete;
>+
>+  int counter = 0;
>+};
>+CountedArg const c;
>+
>+// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref,
>+// so we make extra copies of arguments.
>+
>+void
>+test01()
>+{
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::function_ref<int(CountedArg) const noexcept> r1(f);
>+  std::move_only_function<int(CountedArg) const noexcept> m1(f);
>+  std::copyable_function<int(CountedArg) const noexcept> c1(f);
>+
>+  // Complatible signatures
>+  std::function_ref<int(CountedArg) const noexcept> r2m(m1);
>+  VERIFY( r2m(c) == 2 );
>+  std::function_ref<int(CountedArg) const noexcept> r2c(c1);
>+  VERIFY( r2c(c) == 2 );
>+
>+  std::function_ref<int(CountedArg) const> r3r(r1);
>+  VERIFY( r3r(c) == 2 );
>+  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);
>+  VERIFY( r4m(c) == 2 );
>+  std::function_ref<int(CountedArg)> r4c(c1);
>+  VERIFY( r4c(c) == 2 );
>+
>+  // 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 );
>+}
>+
>+void
>+test02()
>+{
>+  // Constructing move_only_function and copyable_function from function_ref,
>+  // have not chance to restore manager, so we store function_ref inside.
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::function_ref<int(CountedArg) const noexcept> r1(f);
>+
>+  std::move_only_function<int(CountedArg) const noexcept> m1(r1);
>+  VERIFY( m1(c) == 2 );
>+
>+  std::copyable_function<int(CountedArg) const noexcept> c1(r1);
>+  VERIFY( c1(c) == 2 );
>+}
>+
>+void
>+test03()
>+{
>+  struct F
>+  {
>+    int operator()(CountedArg const& arg) noexcept
>+    { return arg.counter; }
>+
>+    int operator()(CountedArg const& arg) const noexcept
>+    { return arg.counter + 1000; }
>+  };
>+
>+  F f;
>+  std::function_ref<int(CountedArg) const> r1(f);
>+  VERIFY( r1(c) == 1001 );
>+
>+  // 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 );
>+  std::move_only_function<int(CountedArg)> m2(r1);
>+  VERIFY( m2(c) == 1002 );
>+
>+  // Call non-const overload as const-qualifed operator() for
>+  // std::function_ref<int(CountedArg)> do.
>+  std::function_ref<int(CountedArg)> r3(f);
>+  VERIFY( r3(c) == 1 );
>+  std::function_ref<int(CountedArg) const> r4(r3);
>+  VERIFY( r4(c) == 2 );
>+  std::move_only_function<int(CountedArg) const> m4(r3);
>+  VERIFY( m4(c) == 2 );
>+}
>+
>+void
>+test04()
>+{
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::function_ref<int(CountedArg)> w1(f);
>+  // function_ref stores function_ref due incompatibile signatures
>+  std::function_ref<int(CountedArg const&)> w2(std::move(w1));
>+  // copy is made when passing to int(CountedArg)
>+  VERIFY( w2(c) == 1 );
>+  // wrapped 3 times
>+  std::function_ref<int(CountedArg)> w3(w2);
>+  VERIFY( w3(c) == 2 );
>+  // wrapped 4 times
>+  std::function_ref<int(CountedArg const&)> w4(w3);
>+  VERIFY( w4(c) == 2 );
>+  // wrapped 5 times
>+  std::function_ref<int(CountedArg)> w5(w4);
>+  VERIFY( w5(c) == 3 );
>+}
>+
>+void
>+test05()
>+{
>+  // No special interoperability with std::function
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::function<int(CountedArg)> f1(f);
>+  std::function_ref<int(CountedArg) const> c1(std::move(f1));
>+  VERIFY( c1(c) == 2 );
>+
>+  std::function_ref<int(CountedArg) const> c2(f);
>+  std::function<int(CountedArg)> f2(c2);
>+  VERIFY( f2(c) == 2 );
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+  test04();
>+  test05();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
>new file mode 100644
>index 00000000000..2940b876954
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
>@@ -0,0 +1,103 @@
>+// { dg-do compile { target c++26 } }
>+
>+#include <functional>
>+#include <type_traits>
>+
>+using std::is_same_v;
>+using std::nontype;
>+using std::nontype_t;
>+using std::function_ref;
>+
>+int i = 0;
>+
>+void f0();
>+void f0n() noexcept;
>+
>+static_assert( is_same_v<decltype(function_ref(f0)),
>+			 function_ref<void()>> );
>+static_assert( is_same_v<decltype(function_ref(f0n)),
>+			 function_ref<void() noexcept>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f0>)),
>+			 function_ref<void()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f0n>)),
>+			 function_ref<void() noexcept>> );
>+
>+void f1(int);
>+void f1n(int) noexcept;
>+
>+static_assert( is_same_v<decltype(function_ref(f1)),
>+			 function_ref<void(int)>> );
>+static_assert( is_same_v<decltype(function_ref(f1n)),
>+			 function_ref<void(int) noexcept>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f1>)),
>+			 function_ref<void(int)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f1n>)),
>+			 function_ref<void(int) noexcept>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)),
>+			 function_ref<void()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)),
>+			 function_ref<void() noexcept>> );
>+
>+void f2(int*, int);
>+void f2n(int*, int) noexcept;
>+
>+static_assert( is_same_v<decltype(function_ref(f2)),
>+			 function_ref<void(int*, int)>> );
>+static_assert( is_same_v<decltype(function_ref(f2n)),
>+			 function_ref<void(int*, int) noexcept>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f2>)),
>+			 function_ref<void(int*, int)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f2n>)),
>+			 function_ref<void(int*, int) noexcept>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)),
>+			 function_ref<void(int)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)),
>+			 function_ref<void(int) noexcept>> );
>+
>+struct S
>+{
>+  int mem;
>+  int f();
>+  int fn() noexcept;
>+
>+  int fc(int) const;
>+  int fcn(int) const noexcept;
>+
>+  int fl(int) &;
>+  int fln(int) & noexcept;
>+
>+  int fcl(float) const&;
>+  int fcln(float) const& noexcept;
>+};
>+S s{};
>+const S cs{};
>+
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)),
>+			 function_ref<int&()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)),
>+			 function_ref<const int&()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)),
>+			 function_ref<int&()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)),
>+			 function_ref<const int&()>> );
>+
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)),
>+			 function_ref<int()>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)),
>+			 function_ref<int() noexcept>> );
>+
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)),
>+			 function_ref<int(int)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)),
>+			 function_ref<int(int) noexcept>> );
>+
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)),
>+			 function_ref<int(int)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)),
>+			 function_ref<int(int) noexcept>> );
>+
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)),
>+			 function_ref<int(float)>> );
>+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)),
>+			 function_ref<int(float) noexcept>> );
>+
>-- 
>2.49.0
>
>



More information about the Libstdc++ mailing list