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
+ 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