[PATCH v2 2/3] libstdc++: Implement C++26 copyable_function [PR119125]

Jonathan Wakely jwakely@redhat.com
Wed May 14 11:06:07 GMT 2025


On 14/05/25 10:48 +0200, Tomasz Kamiński wrote:
>This patch implements C++26 copyable_function as specified in P2548R6.
>It also implements LWG 4255 that adjust move_only_function so constructing
>from empty copyable_function, produces empty functor. This falls from
>existing checks, after specializing __is_polymorphic_function_v for
>copyable_function specializations.
>
>For compatible invoker signatures, the move_only_function may be constructed
>from copyable_funciton without double indirection. To achieve that we derive
>_Cpy_base from _Mo_base, and specialize __is_polymorphic_function_v for
>copyable_function. Similary copyable_functions with compatible signatures
>can be converted without double indirection.
>
>As we starting to use _Op::_Copy operation from the _M_manage function,
>invocations of that functions may now throw exceptions, so noexcept needs
>to be removed from the signature of stored _M_manage pointers. This also
>affects operations in _Mo_base, however we already wrap _M_manage invocations
>in noexcept member functions (_M_move, _M_destroy, swap).
>
>	PR libstdc++/119125
>
>libstdc++-v3/ChangeLog:
>
>	* doc/doxygen/stdheader.cc: Addded cpyfunc_impl.h header.
>	* include/Makefile.am: Add bits cpyfunc_impl.h.
>	* include/Makefile.in: Add bits cpyfunc_impl.h.
>	* include/bits/cpyfunc_impl.h: New file.
>	* include/bits/mofunc_impl.h: Mention LWG 4255.
>	* include/bits/move_only_function.h: Update header description
>	and change guard to __cplusplus > 202002L.
>	(_Manager::_Func): Remove noexcept.
>	(std::__is_polymorphic_function_v<move_only_function<_Tp>>)
>	(__variant::_Never_valueless_alt<std::move_only_function<_Signature...>>)
>	(move_only_function) [__glibcxx_move_only_function]: Adjust guard.
>	(std::__is_polymorphic_function_v<copyable_function<_Tp>>)
>	(__variant::_Never_valueless_alt<std::copyable_function<_Signature...>>)
>	(__polyfunc::_Cpy_base, std::copyable_function)
>	[__glibcxx_copyable_function]: Define.
>	* include/bits/version.def: Define copyable_function.
>	* include/bits/version.h: Regenerate.
>	* include/std/functional: Define __cpp_lib_copyable_function.
>	* src/c++23/std.cc.in (copyable_function)
>	[__cpp_lib_copyable_function]: Export.
>	* testsuite/20_util/copyable_function/call.cc: New test based on
>	move_only_function tests.
>	* testsuite/20_util/copyable_function/cons.cc: New test based on
>	move_only_function tests.
>	* testsuite/20_util/copyable_function/conv.cc: New test based on
>	move_only_function tests.
>	* testsuite/20_util/copyable_function/copy.cc: New test.
>	* testsuite/20_util/copyable_function/move.cc: New test based on
>	move_only_function tests.
>---
>In addition to fixing formatting and typos, this patch adds export of
>the copyable_function to std module.
>
> libstdc++-v3/doc/doxygen/stdheader.cc         |   1 +
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/bits/cpyfunc_impl.h      | 269 ++++++++++++++++++
> libstdc++-v3/include/bits/mofunc_impl.h       |   4 +
> .../include/bits/move_only_function.h         |  94 +++++-
> libstdc++-v3/include/bits/version.def         |  10 +
> libstdc++-v3/include/bits/version.h           |  10 +
> libstdc++-v3/include/std/functional           |   1 +
> libstdc++-v3/src/c++23/std.cc.in              |   3 +
> .../20_util/copyable_function/call.cc         | 224 +++++++++++++++
> .../20_util/copyable_function/cons.cc         | 126 ++++++++
> .../20_util/copyable_function/conv.cc         | 251 ++++++++++++++++
> .../20_util/copyable_function/copy.cc         | 154 ++++++++++
> .../20_util/copyable_function/move.cc         | 120 ++++++++
> 15 files changed, 1264 insertions(+), 5 deletions(-)
> create mode 100644 libstdc++-v3/include/bits/cpyfunc_impl.h
> create mode 100644 libstdc++-v3/testsuite/20_util/copyable_function/call.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/copyable_function/cons.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/copyable_function/copy.cc
> create mode 100644 libstdc++-v3/testsuite/20_util/copyable_function/move.cc
>
>diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc b/libstdc++-v3/doc/doxygen/stdheader.cc
>index 3ee825feb66..8a201334410 100644
>--- a/libstdc++-v3/doc/doxygen/stdheader.cc
>+++ b/libstdc++-v3/doc/doxygen/stdheader.cc
>@@ -54,6 +54,7 @@ void init_map()
>     headers["function.h"]               = "functional";
>     headers["functional_hash.h"]        = "functional";
>     headers["mofunc_impl.h"]            = "functional";
>+    headers["cpyfunc_impl.h"]           = "functional";
>     headers["move_only_function.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 1140fa0dffd..5cc13381b02 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -194,6 +194,7 @@ bits_headers = \
> 	${bits_srcdir}/chrono_io.h \
> 	${bits_srcdir}/codecvt.h \
> 	${bits_srcdir}/cow_string.h \
>+	${bits_srcdir}/cpyfunc_impl.h \
> 	${bits_srcdir}/deque.tcc \
> 	${bits_srcdir}/erase_if.h \
> 	${bits_srcdir}/formatfwd.h \
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index c96e981acd6..6e5e97aa236 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in
>@@ -547,6 +547,7 @@ bits_freestanding = \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/chrono_io.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/codecvt.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/cow_string.h \
>+@GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/cpyfunc_impl.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/deque.tcc \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/erase_if.h \
> @GLIBCXX_HOSTED_TRUE@	${bits_srcdir}/formatfwd.h \
>diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h b/libstdc++-v3/include/bits/cpyfunc_impl.h
>new file mode 100644
>index 00000000000..90a6938ae24
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
>@@ -0,0 +1,269 @@
>+// Implementation of std::copyable_function -*- 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/cpyfunc_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
>+
>+#ifdef _GLIBCXX_MOF_REF
>+# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF
>+#else
>+# define _GLIBCXX_MOF_REF
>+# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV &
>+#endif
>+
>+#define _GLIBCXX_MOF_CV_REF _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  /**
>+   *  @brief Polymorphic copyable function wrapper.
>+   *  @ingroup functors
>+   *  @since C++26
>+   *  @headerfile functional
>+   *
>+   *  The `std::copyable_function` class template is a call wrapper similar
>+   *  to `std::function`, but it does not provide information about target,

s/target/its target/

>+   *  and preserves constness.
>+   *
>+   *  It also supports const-qualification, ref-qualification, and
>+   *  no-throw guarantees. The qualifications and exception-specification
>+   *  of the `copyable_function::operator()` member function are respected
>+   *  when invoking the target function.
>+   */
>+  template<typename _Res, typename... _ArgTypes, bool _Noex>
>+    class copyable_function<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
>+			    _GLIBCXX_MOF_REF noexcept(_Noex)>
>+    : __polyfunc::_Cpy_base
>+    {
>+      using _Base = __polyfunc::_Cpy_base;
>+      using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
>+      using _Signature = _Invoker::_Signature;
>+
>+      template<typename _Tp>
>+	using __callable
>+	  = __conditional_t<_Noex,
>+			    is_nothrow_invocable_r<_Res, _Tp, _ArgTypes...>,
>+			    is_invocable_r<_Res, _Tp, _ArgTypes...>>;
>+
>+      // [func.wrap.copy.con]/1 is-callable-from<VT>
>+      template<typename _Vt>
>+	static constexpr bool __is_callable_from
>+	  = __and_v<__callable<_Vt _GLIBCXX_MOF_CV_REF>,
>+		    __callable<_Vt _GLIBCXX_MOF_INV_QUALS>>;
>+
>+    public:
>+      using result_type = _Res;
>+
>+      /// Creates an empty object.
>+      copyable_function() noexcept { }
>+
>+      /// Creates an empty object.
>+      copyable_function(nullptr_t) noexcept { }
>+
>+      /// Moves the target object, leaving the source empty.
>+      copyable_function(copyable_function&& __x) noexcept
>+      : _Base(static_cast<_Base&&>(__x)),
>+	_M_invoke(std::__exchange(__x._M_invoke, nullptr))
>+      { }
>+
>+      /// Copies the target object.
>+      copyable_function(copyable_function const& __x)
>+      : _Base(static_cast<const _Base&>(__x)),
>+	_M_invoke(__x._M_invoke)
>+      { }
>+
>+      /// Stores a target object initialized from the argument.
>+      template<typename _Fn, typename _Vt = decay_t<_Fn>>
>+	requires (!is_same_v<_Vt, copyable_function>)
>+	  && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt>
>+	copyable_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>())
>+	{
>+	  static_assert(is_copy_constructible_v<_Vt>);
>+	  if constexpr (is_function_v<remove_pointer_t<_Vt>>
>+			|| is_member_pointer_v<_Vt>
>+			|| __is_polymorphic_function_v<_Vt>)
>+	    {
>+	      if (__f == nullptr)
>+		return;
>+	    }
>+
>+	  if constexpr (!__is_polymorphic_function_v<_Vt>
>+			  || !__polyfunc::__is_invoker_convertible<_Vt, copyable_function>())
>+	    {
>+	      _M_init<_Vt>(std::forward<_Fn>(__f));
>+	      _M_invoke = _Invoker::template _S_storage<_Vt _GLIBCXX_MOF_INV_QUALS>();
>+	    }
>+	  else if constexpr (is_lvalue_reference_v<_Fn>)
>+	    {
>+	      _M_copy(__polyfunc::__base_of(__f));
>+	      _M_invoke = __polyfunc::__invoker_of(__f);
>+	    }
>+	  else
>+	    {
>+	      _M_move(__polyfunc::__base_of(__f));
>+	      _M_invoke = std::__exchange(__polyfunc::__invoker_of(__f), nullptr);
>+	    }
>+	}
>+
>+      /// Stores a target object initialized from the arguments.
>+      template<typename _Tp, typename... _Args>
>+	requires is_constructible_v<_Tp, _Args...>
>+	  && __is_callable_from<_Tp>
>+	explicit
>+	copyable_function(in_place_type_t<_Tp>, _Args&&... __args)
>+	noexcept(_S_nothrow_init<_Tp, _Args...>())
>+	: _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>())
>+	{
>+	  static_assert(is_same_v<decay_t<_Tp>, _Tp>);
>+	  static_assert(is_copy_constructible_v<_Tp>);
>+	  _M_init<_Tp>(std::forward<_Args>(__args)...);
>+	}
>+
>+      /// Stores a target object initialized from the arguments.
>+      template<typename _Tp, typename _Up, typename... _Args>
>+	requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
>+	  && __is_callable_from<_Tp>
>+	explicit
>+	copyable_function(in_place_type_t<_Tp>, initializer_list<_Up> __il,
>+			   _Args&&... __args)
>+	noexcept(_S_nothrow_init<_Tp, initializer_list<_Up>&, _Args...>())
>+	: _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>())
>+	{
>+	  static_assert(is_same_v<decay_t<_Tp>, _Tp>);
>+	  static_assert(is_copy_constructible_v<_Tp>);
>+	  _M_init<_Tp>(__il, std::forward<_Args>(__args)...);
>+	}
>+
>+      /// Stores a new target object, leaving `x` empty.
>+      copyable_function&
>+      operator=(copyable_function&& __x) noexcept
>+      {
>+	// Standard requires support of self assigment, by specifying it as
>+	// copy and swap.
>+	if (this != addressof(__x)) [[likely]]

Qualify as std::addressof

This is the only important change, the comments below aren't necessary
to act on.

OK for trunk with the comment change noted above and the
std::addressof change here.

>+	  {
>+	    _Base::operator=(static_cast<_Base&&>(__x));
>+	    _M_invoke = std::__exchange(__x._M_invoke, nullptr);
>+	  }
>+	return *this;
>+      }
>+
>+      /// Stores a copy of the source target object
>+      copyable_function&
>+      operator=(const copyable_function& __x)
>+      {
>+	copyable_function(__x).swap(*this);
>+	return *this;
>+      }
>+
>+      /// Destroys the target object (if any).
>+      copyable_function&
>+      operator=(nullptr_t) noexcept
>+      {
>+	_M_reset();
>+	_M_invoke = nullptr;
>+	return *this;
>+      }
>+
>+      /// Stores a new target object, initialized from the argument.
>+      template<typename _Fn>
>+	requires is_constructible_v<copyable_function, _Fn>
>+	copyable_function&
>+	operator=(_Fn&& __f)
>+	noexcept(is_nothrow_constructible_v<copyable_function, _Fn>)
>+	{
>+	  copyable_function(std::forward<_Fn>(__f)).swap(*this);
>+	  return *this;
>+	}
>+
>+      ~copyable_function() = default;
>+
>+      /// True if a target object is present, false otherwise.
>+      explicit operator bool() const noexcept
>+      { return _M_invoke != nullptr; }
>+
>+      /** Invoke the target object.
>+       *
>+       * The target object will be invoked using the supplied arguments,
>+       * and as an lvalue or rvalue, and as const or non-const, as dictated
>+       * by the template arguments of the `copyable_function` specialization.
>+       *
>+       * @pre Must not be empty.
>+       */
>+      _Res
>+      operator()(_ArgTypes... __args) _GLIBCXX_MOF_CV_REF noexcept(_Noex)
>+      {
>+	__glibcxx_assert(*this != nullptr);
>+	return _M_invoke(this->_M_storage, std::forward<_ArgTypes>(__args)...);
>+      }
>+
>+      /// Exchange the target objects (if any).
>+      void
>+      swap(copyable_function& __x) noexcept
>+      {
>+	_Base::swap(__x);
>+	std::swap(_M_invoke, __x._M_invoke);
>+      }
>+
>+      /// Exchange the target objects (if any).
>+      friend void
>+      swap(copyable_function& __x, copyable_function& __y) noexcept
>+      { __x.swap(__y); }
>+
>+      /// Check for emptiness by comparing with `nullptr`.
>+      friend bool
>+      operator==(const copyable_function& __x, nullptr_t) noexcept
>+      { return __x._M_invoke == nullptr; }
>+
>+    private:
>+      typename _Invoker::__storage_func_t _M_invoke = nullptr;
>+
>+      template<typename _Func>
>+	friend auto&
>+	__polyfunc::__invoker_of(_Func&) noexcept;
>+
>+      template<typename _Func>
>+	friend auto&
>+	__polyfunc::__base_of(_Func&) noexcept;
>+
>+      template<typename _Dst, typename _Src>
>+	friend consteval bool
>+	__polyfunc::__is_invoker_convertible() noexcept;
>+    };
>+
>+#undef _GLIBCXX_MOF_CV_REF
>+#undef _GLIBCXX_MOF_CV
>+#undef _GLIBCXX_MOF_REF
>+#undef _GLIBCXX_MOF_INV_QUALS
>+
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>diff --git a/libstdc++-v3/include/bits/mofunc_impl.h b/libstdc++-v3/include/bits/mofunc_impl.h
>index 5eb4b5a0047..509f596f8e3 100644
>--- a/libstdc++-v3/include/bits/mofunc_impl.h
>+++ b/libstdc++-v3/include/bits/mofunc_impl.h
>@@ -101,6 +101,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt>
> 	move_only_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>())
> 	{
>+	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+	  // 4255. move_only_function constructor should recognize empty
>+	  //       copyable_functions
> 	  if constexpr (is_function_v<remove_pointer_t<_Vt>>
> 			|| is_member_pointer_v<_Vt>
> 			|| __is_polymorphic_function_v<_Vt>)
>@@ -108,6 +111,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	      if (__f == nullptr)
> 		return;
> 	    }
>+
> 	  if constexpr (__is_polymorphic_function_v<_Vt>
> 			  && __polyfunc::__is_invoker_convertible<_Vt, move_only_function>())
> 	    {
>diff --git a/libstdc++-v3/include/bits/move_only_function.h b/libstdc++-v3/include/bits/move_only_function.h
>index 305fe986818..ecaded79d37 100644
>--- a/libstdc++-v3/include/bits/move_only_function.h
>+++ b/libstdc++-v3/include/bits/move_only_function.h
>@@ -1,4 +1,4 @@
>-// Implementation of std::move_only_function -*- C++ -*-
>+// Implementation of std::move_only_function and std::copyable_function -*- C++ -*-
>
> // Copyright The GNU Toolchain Authors.
> //
>@@ -36,7 +36,7 @@
>
> #include <bits/version.h>
>
>-#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED
>+#if __cplusplus > 202002L && _GLIBCXX_HOSTED

Could this be:
#if defined __glibcxx_move_only_function || defined __glibcxx_copyable_function
?

Or don't bother checking it here at all, and rely on the fact that
<functional> only includes it when needed?


>
> #include <bits/invoke.h>
> #include <bits/utility.h>
>@@ -99,7 +99,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>      template<typename _Tp>
>        [[__gnu__::__always_inline__]]
>-       _Tp*
>+       _Tp*

What changed here? I'm not seeing any diff in this diff!

Even the whitespace looks unchanged.

>        _M_ptr() const noexcept
>        {
> 	 if constexpr (!_S_stored_locally<remove_const_t<_Tp>>())
>@@ -200,7 +200,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      };
>
>     // A function that performs operation __op on the __target and possibly __src.
>-    using _Func = void (*)(_Op __op, _Storage& __target, const _Storage* __src) noexcept;
>+    using _Func = void (*)(_Op __op, _Storage& __target, const _Storage* __src);
>
>     // The no-op manager function for objects with no target.
>     static void _S_empty(_Op, _Storage&, const _Storage*) noexcept { }
>@@ -377,11 +377,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      { _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); }
>
>      _Manager::_Func _M_manage;
>+
>+#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED
>+     friend class _Cpy_base;
>+#endif // __glibcxx_copyable_function
>    };
>
> } // namespace __polyfunc
>   /// @endcond
>
>+#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED
>   template<typename... _Signature>
>     class move_only_function; // not defined
>
>@@ -401,10 +406,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { };
>   }  // namespace __detail::__variant
>   /// @endcond
>+#endif // __glibcxx_move_only_function
>+
>+#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED
>+  /// @cond undocumented
>+  namespace __polyfunc
>+  {
>+     class _Cpy_base : public _Mo_base
>+     {
>+     protected:
>+       _Cpy_base() = default;
>+
>+       template<typename _Tp, typename... _Args>
>+	 void
>+	 _M_init(_Args&&... __args)
>+	 noexcept(_S_nothrow_init<_Tp, _Args...>())
>+	 {
>+	   _M_storage._M_init<_Tp>(std::forward<_Args>(__args)...);
>+	   _M_manage = _Manager::_S_select<true, _Tp>();
>+	 }
>+
>+      void
>+      _M_copy(_Cpy_base const& __x)
>+      {
>+	using _Op = _Manager::_Op;
>+	__x._M_manage(_Op::_Copy, _M_storage, &__x._M_storage);
>+	_M_manage = __x._M_manage;
>+      }
>+
>+      _Cpy_base(_Cpy_base&&) = default;
>+
>+      _Cpy_base(_Cpy_base const& __x)
>+      { _M_copy(__x); }
>+
>+      _Cpy_base&
>+      operator=(_Cpy_base&&) = default;
>+
>+      _Cpy_base&
>+      // Needs to use copy and swap for exception guarantees.
>+      operator=(_Cpy_base const&) = delete;
>+    };
>+  } // namespace __polyfunc
>+  /// @endcond
>+
>+  template<typename... _Signature>
>+    class copyable_function; // not defined
>+
>+  template<typename _Tp>
>+    constexpr bool __is_polymorphic_function_v<copyable_function<_Tp>> = true;
>+
>+    namespace __detail::__variant
>+  {
>+    template<typename> struct _Never_valueless_alt; // see <variant>
>+
>+    // Provide the strong exception-safety guarantee when emplacing a
>+    // copyable_function into a variant.
>+    template<typename... _Signature>
>+      struct _Never_valueless_alt<std::copyable_function<_Signature...>>
>+      : true_type
>+      { };
>+  }  // namespace __detail::__variant
>+#endif // __glibcxx_copyable_function
>
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
>
>+#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED
> #include "mofunc_impl.h"
> #define _GLIBCXX_MOF_CV const
> #include "mofunc_impl.h"
>@@ -418,6 +485,23 @@ _GLIBCXX_END_NAMESPACE_VERSION
> #define _GLIBCXX_MOF_CV const
> #define _GLIBCXX_MOF_REF &&
> #include "mofunc_impl.h"
>-
> #endif // __glibcxx_move_only_function
>+
>+#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED
>+#include "cpyfunc_impl.h"
>+#define _GLIBCXX_MOF_CV const
>+#include "cpyfunc_impl.h"
>+#define _GLIBCXX_MOF_REF &
>+#include "cpyfunc_impl.h"
>+#define _GLIBCXX_MOF_REF &&
>+#include "cpyfunc_impl.h"
>+#define _GLIBCXX_MOF_CV const
>+#define _GLIBCXX_MOF_REF &
>+#include "cpyfunc_impl.h"
>+#define _GLIBCXX_MOF_CV const
>+#define _GLIBCXX_MOF_REF &&
>+#include "cpyfunc_impl.h"
>+#endif // __glibcxx_copyable_function
>+
>+#endif // __cplusplus > 202002L && _GLIBCXX_HOSTED
> #endif // _GLIBCXX_MOVE_ONLY_FUNCTION_H
>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
>index 2d34a8dff7f..21f16e26ba4 100644
>--- a/libstdc++-v3/include/bits/version.def
>+++ b/libstdc++-v3/include/bits/version.def
>@@ -1747,6 +1747,16 @@ ftms = {
>   };
> };
>
>+ftms = {
>+  name = copyable_function;
>+  values = {
>+    v = 202306;
>+    cxxmin = 26;
>+    hosted = yes;
>+  };
>+};
>+
>+
> ftms = {
>   name = out_ptr;
>   values = {
>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
>index 24831f70b41..48a090c14a3 100644
>--- a/libstdc++-v3/include/bits/version.h
>+++ b/libstdc++-v3/include/bits/version.h
>@@ -1948,6 +1948,16 @@
> #endif /* !defined(__cpp_lib_move_only_function) && defined(__glibcxx_want_move_only_function) */
> #undef __glibcxx_want_move_only_function
>
>+#if !defined(__cpp_lib_copyable_function)
>+# if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
>+#  define __glibcxx_copyable_function 202306L
>+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_copyable_function)
>+#   define __cpp_lib_copyable_function 202306L
>+#  endif
>+# endif
>+#endif /* !defined(__cpp_lib_copyable_function) && defined(__glibcxx_want_copyable_function) */
>+#undef __glibcxx_want_copyable_function
>+
> #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 1077e9678d1..46179998eeb 100644
>--- a/libstdc++-v3/include/std/functional
>+++ b/libstdc++-v3/include/std/functional
>@@ -80,6 +80,7 @@
> #define __glibcxx_want_bind_front
> #define __glibcxx_want_bind_back
> #define __glibcxx_want_constexpr_functional
>+#define __glibcxx_want_copyable_function
> #define __glibcxx_want_invoke
> #define __glibcxx_want_invoke_r
> #define __glibcxx_want_move_only_function
>diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
>index d45ae632ace..417c8a1a562 100644
>--- a/libstdc++-v3/src/c++23/std.cc.in
>+++ b/libstdc++-v3/src/c++23/std.cc.in
>@@ -1411,6 +1411,9 @@ export namespace std
>   using std::modulus;
> #if __cpp_lib_move_only_function
>   using std::move_only_function;
>+#endif
>+#if __cpp_lib_copyable_function
>+  using std::copyable_function;
> #endif
>   using std::multiplies;
>   using std::negate;
>diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
>new file mode 100644
>index 00000000000..cf997577f62
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
>@@ -0,0 +1,224 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-effective-target hosted }
>+
>+#include <functional>
>+#include <utility>
>+#include <testsuite_hooks.h>
>+
>+using std::copyable_function;
>+
>+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<copyable_function<void()>>> );
>+static_assert( is_same_v<int, invoke_result_t<copyable_function<int()>>> );
>+static_assert( is_same_v<int&, invoke_result_t<copyable_function<int&()>>> );
>+
>+// With const qualifier
>+static_assert( ! is_invocable_v< copyable_function<void()> const > );
>+static_assert( ! is_invocable_v< copyable_function<void()> const &> );
>+static_assert( is_invocable_v< copyable_function<void() const> > );
>+static_assert( is_invocable_v< copyable_function<void() const> &> );
>+static_assert( is_invocable_v< copyable_function<void() const> const > );
>+static_assert( is_invocable_v< copyable_function<void() const> const &> );
>+
>+// With no ref-qualifier
>+static_assert( is_invocable_v< copyable_function<void()> > );
>+static_assert( is_invocable_v< copyable_function<void()> &> );
>+static_assert( is_invocable_v< copyable_function<void() const> > );
>+static_assert( is_invocable_v< copyable_function<void() const> &> );
>+static_assert( is_invocable_v< copyable_function<void() const> const > );
>+static_assert( is_invocable_v< copyable_function<void() const> const &> );
>+
>+// With & ref-qualifier
>+static_assert( ! is_invocable_v< copyable_function<void()&> > );
>+static_assert( is_invocable_v< copyable_function<void()&> &> );
>+static_assert( is_invocable_v< copyable_function<void() const&> > );
>+static_assert( is_invocable_v< copyable_function<void() const&> &> );
>+static_assert( is_invocable_v< copyable_function<void() const&> const > );
>+static_assert( is_invocable_v< copyable_function<void() const&> const &> );
>+
>+// With && ref-qualifier
>+static_assert( is_invocable_v< copyable_function<void()&&> > );
>+static_assert( ! is_invocable_v< copyable_function<void()&&> &> );
>+static_assert( is_invocable_v< copyable_function<void() const&&> > );
>+static_assert( ! is_invocable_v< copyable_function<void() const&&> &> );
>+static_assert( is_invocable_v< copyable_function<void() const&&> const > );
>+static_assert( ! is_invocable_v< copyable_function<void() const&&> const &> );
>+
>+// With noexcept-specifier
>+static_assert( ! is_nothrow_invocable_v< copyable_function<void()> > );
>+static_assert( ! is_nothrow_invocable_v< copyable_function<void() noexcept(false)> > );
>+static_assert( is_nothrow_invocable_v< copyable_function<void() noexcept> > );
>+static_assert( is_nothrow_invocable_v< copyable_function<void()& noexcept>& > );
>+
>+void
>+test01()
>+{
>+  struct F
>+  {
>+    int operator()() { return 0; }
>+    int operator()() const { return 1; }
>+  };
>+
>+  copyable_function<int()> f0{F{}};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  copyable_function<int() const> f1{F{}};
>+  VERIFY( f1() == 1 );
>+  VERIFY( std::as_const(f1)() == 1 );
>+  VERIFY( std::move(f1)() == 1 );
>+  VERIFY( std::move(std::as_const(f1))() == 1 );
>+
>+  copyable_function<int()&> f2{F{}};
>+  VERIFY( f2() == 0 );
>+  // Not rvalue-callable: std::move(f2)()
>+
>+  copyable_function<int() const&> f3{F{}};
>+  VERIFY( f3() == 1 );
>+  VERIFY( std::as_const(f3)() == 1 );
>+  VERIFY( std::move(f3)() == 1 );
>+  VERIFY( std::move(std::as_const(f3))() == 1 );
>+
>+  copyable_function<int()&&> f4{F{}};
>+  // Not lvalue-callable: f4()
>+  VERIFY( std::move(f4)() == 0 );
>+
>+  copyable_function<int() const&&> f5{F{}};
>+  // Not lvalue-callable: f5()
>+  VERIFY( std::move(f5)() == 1 );
>+  VERIFY( std::move(std::as_const(f5))() == 1 );
>+}
>+
>+void
>+test02()
>+{
>+  struct F
>+  {
>+    int operator()() & { return 0; }
>+    int operator()() && { return 1; }
>+  };
>+
>+  copyable_function<int()> f0{F{}};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  copyable_function<int()&&> f1{F{}};
>+  // Not lvalue callable: f1()
>+  VERIFY( std::move(f1)() == 1 );
>+
>+  copyable_function<int()&> f2{F{}};
>+  VERIFY( f2() == 0 );
>+  // Not rvalue-callable: std::move(f2)()
>+}
>+
>+void
>+test03()
>+{
>+  struct F
>+  {
>+    int operator()() const & { return 0; }
>+    int operator()() && { return 1; }
>+  };
>+
>+  copyable_function<int()> f0{F{}};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  copyable_function<int()&&> f1{F{}};
>+  // Not lvalue callable: f1()
>+  VERIFY( std::move(f1)() == 1 );
>+
>+  copyable_function<int() const> f2{F{}};
>+  VERIFY( f2() == 0 );
>+  VERIFY( std::as_const(f2)() == 0 );
>+  VERIFY( std::move(f2)() == 0 );
>+  VERIFY( std::move(std::as_const(f2))() == 0 );
>+
>+  copyable_function<int() const &&> f3{F{}};
>+  // Not lvalue callable: f3()
>+  VERIFY( std::move(f3)() == 0 );
>+  VERIFY( std::move(std::as_const(f3))() == 0 );
>+
>+  copyable_function<int() const &> f4{F{}};
>+  VERIFY( f4() == 0 );
>+  VERIFY( std::as_const(f4)() == 0 );
>+  // Not rvalue-callable: std::move(f4)()
>+}
>+
>+void
>+test04()
>+{
>+  struct F
>+  {
>+    int operator()() & { return 0; }
>+    int operator()() && { return 1; }
>+    int operator()() const & { return 2; }
>+    int operator()() const && { return 3; }
>+  };
>+
>+  copyable_function<int()> f0{F{}};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  copyable_function<int()&> f1{F{}};
>+  VERIFY( f1() == 0 );
>+  // Not rvalue-callable: std::move(f1)()
>+
>+  copyable_function<int()&&> f2{F{}};
>+  // Not lvalue callable: f2()
>+  VERIFY( std::move(f2)() == 1 );
>+
>+  copyable_function<int() const> f3{F{}};
>+  VERIFY( f3() == 2 );
>+  VERIFY( std::as_const(f3)() == 2 );
>+  VERIFY( std::move(f3)() == 2 );
>+  VERIFY( std::move(std::as_const(f3))() == 2 );
>+
>+  copyable_function<int() const &> f4{F{}};
>+  VERIFY( f4() == 2 );
>+  VERIFY( std::as_const(f4)() == 2 );
>+  // Not rvalue-callable: std::move(f4)()
>+
>+  copyable_function<int() const &&> f5{F{}};
>+  // Not lvalue callable: f5()
>+  VERIFY( std::move(f5)() == 3 );
>+  VERIFY( std::move(std::as_const(f5))() == 3 );
>+}
>+
>+void
>+test05()
>+{
>+  int (*fp)() = [] { return 0; };
>+  copyable_function<int()> f0{fp};
>+  VERIFY( f0() == 0 );
>+  VERIFY( std::move(f0)() == 0 );
>+
>+  const copyable_function<int() const> f1{fp};
>+  VERIFY( f1() == 0 );
>+  VERIFY( std::move(f1)() == 0 );
>+}
>+
>+struct Incomplete;
>+
>+void
>+test_params()
>+{
>+  std::copyable_function<void(Incomplete)> f1;
>+  std::copyable_function<void(Incomplete&)> f2;
>+  std::copyable_function<void(Incomplete&&)> f3;
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+  test04();
>+  test05();
>+  test_params();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc
>new file mode 100644
>index 00000000000..8d422dcff8a
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc
>@@ -0,0 +1,126 @@
>+// { dg-do compile { target c++26 } }
>+// { dg-require-effective-target hosted }
>+// { dg-add-options no_pch }
>+
>+#include <functional>
>+
>+#ifndef __cpp_lib_copyable_function
>+# error "Feature-test macro for copyable_function missing in <functional>"
>+#elif __cpp_lib_copyable_function != 202306L
>+# error "Feature-test macro for copyable_function has wrong value in <functional>"
>+#endif
>+
>+using std::copyable_function;
>+
>+using std::is_constructible_v;
>+using std::is_copy_constructible_v;
>+using std::is_nothrow_default_constructible_v;
>+using std::is_nothrow_move_constructible_v;
>+using std::is_nothrow_constructible_v;
>+using std::nullptr_t;
>+using std::in_place_type_t;
>+
>+static_assert( is_nothrow_default_constructible_v<copyable_function<void()>> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void()>, nullptr_t> );
>+static_assert( is_nothrow_move_constructible_v<copyable_function<void()>> );
>+static_assert( is_copy_constructible_v<copyable_function<void()>> );
>+
>+static_assert( is_constructible_v<copyable_function<void()>, void()> );
>+static_assert( is_constructible_v<copyable_function<void()>, void(&)()> );
>+static_assert( is_constructible_v<copyable_function<void()>, void(*)()> );
>+static_assert( is_constructible_v<copyable_function<void()>, int()> );
>+static_assert( is_constructible_v<copyable_function<void()>, int(&)()> );
>+static_assert( is_constructible_v<copyable_function<void()>, int(*)()> );
>+static_assert( ! is_constructible_v<copyable_function<void()>, void(int)> );
>+static_assert( is_constructible_v<copyable_function<void(int)>, void(int)> );
>+
>+static_assert( is_constructible_v<copyable_function<void(int)>,
>+				  in_place_type_t<void(*)(int)>, void(int)> );
>+
>+static_assert( is_constructible_v<copyable_function<void()>,
>+				  void() noexcept> );
>+static_assert( is_constructible_v<copyable_function<void() noexcept>,
>+				  void() noexcept> );
>+static_assert( ! is_constructible_v<copyable_function<void() noexcept>,
>+				    void() > );
>+
>+struct Q
>+{
>+  void operator()() const &;
>+  void operator()() &&;
>+};
>+
>+static_assert( is_constructible_v<copyable_function<void()>, Q> );
>+static_assert( is_constructible_v<copyable_function<void() const>, Q> );
>+static_assert( is_constructible_v<copyable_function<void() &>, Q> );
>+static_assert( is_constructible_v<copyable_function<void() const &>, Q> );
>+static_assert( is_constructible_v<copyable_function<void() &&>, Q> );
>+static_assert( is_constructible_v<copyable_function<void() const &&>, Q> );
>+
>+struct R
>+{
>+  void operator()() &;
>+  void operator()() &&;
>+};
>+
>+static_assert( is_constructible_v<copyable_function<void()>, R> );
>+static_assert( is_constructible_v<copyable_function<void()&>, R> );
>+static_assert( is_constructible_v<copyable_function<void()&&>, R> );
>+static_assert( ! is_constructible_v<copyable_function<void() const>, R> );
>+static_assert( ! is_constructible_v<copyable_function<void() const&>, R> );
>+static_assert( ! is_constructible_v<copyable_function<void() const&&>, R> );
>+
>+// The following nothrow-constructible guarantees are a GCC extension,
>+// not required by the standard.
>+
>+static_assert( is_nothrow_constructible_v<copyable_function<void()>, void()> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void(int)>,
>+					  in_place_type_t<void(*)(int)>,
>+					  void(int)> );
>+
>+// These types are all small and nothrow move constructible
>+struct F { void operator()(); };
>+struct G { void operator()() const; };
>+static_assert( is_nothrow_constructible_v<copyable_function<void()>, F> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void()>, G> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void() const>, G> );
>+
>+struct H {
>+  H(int);
>+  H(int, int) noexcept;
>+  void operator()() noexcept;
>+};
>+static_assert( is_nothrow_constructible_v<copyable_function<void()>, H> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>,
>+					  H> );
>+static_assert( ! is_nothrow_constructible_v<copyable_function<void() noexcept>,
>+					    in_place_type_t<H>, int> );
>+static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>,
>+					  in_place_type_t<H>, int, int> );
>+
>+struct I {
>+  I(int, const char*);
>+  I(std::initializer_list<char>);
>+  int operator()() const noexcept;
>+};
>+
>+static_assert( is_constructible_v<copyable_function<void()>,
>+				  std::in_place_type_t<I>,
>+				  int, const char*> );
>+static_assert( is_constructible_v<copyable_function<void()>,
>+				  std::in_place_type_t<I>,
>+				  std::initializer_list<char>> );
>+
>+void
>+test_instantiation()
>+{
>+  // Instantiate the constructor bodies
>+  copyable_function<void()> f0;
>+  copyable_function<void()> f1(nullptr);
>+  copyable_function<void()> f2( I(1, "two") );
>+  copyable_function<void()> f3(std::in_place_type<I>, 3, "four");
>+  copyable_function<void()> f4(std::in_place_type<I>, // PR libstdc++/102825
>+				{ 'P', 'R', '1', '0', '2', '8', '2', '5'});
>+  auto f5 = std::move(f4);
>+  f4 = std::move(f5);
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
>new file mode 100644
>index 00000000000..e678e166172
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
>@@ -0,0 +1,251 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-effective-target hosted }
>+
>+#include <functional>
>+#include <testsuite_hooks.h>
>+
>+using std::copyable_function;
>+
>+static_assert( !std::is_constructible_v<std::copyable_function<void()>,
>+					std::copyable_function<void()&>> );
>+static_assert( !std::is_constructible_v<std::copyable_function<void()>,
>+					std::copyable_function<void()&&>> );
>+static_assert( !std::is_constructible_v<std::copyable_function<void()&>,
>+					std::copyable_function<void()&&>> );
>+static_assert( !std::is_constructible_v<std::copyable_function<void() const>,
>+					std::copyable_function<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;
>+
>+// When copyable_function or move_only_function is constructed from other copyable_function,
>+// the compiler can avoid double indirection per C++26 [func.wrap.general] p2.
>+
>+void
>+test01()
>+{
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::copyable_function<int(CountedArg) const noexcept> c1(f);
>+  using CF = std::copyable_function<int(CountedArg) const noexcept>;
>+  VERIFY( c1(c) == 1 );
>+
>+  std::copyable_function<int(CountedArg) const> c2a(c1);
>+  VERIFY( c2a(c) == 1 );
>+
>+  std::copyable_function<int(CountedArg) const> c2b(static_cast<CF>(c1));
>+  VERIFY( c2b(c) == 1 );
>+
>+  std::move_only_function<int(CountedArg) const> m2a(c1);
>+  VERIFY( m2a(c) == 1 );
>+
>+  std::move_only_function<int(CountedArg) const> m2b(static_cast<CF>(c1));
>+  VERIFY( m2b(c) == 1 );
>+
>+  std::copyable_function<int(CountedArg)> c3a(c1);
>+  VERIFY( c3a(c) == 1 );
>+
>+  std::copyable_function<int(CountedArg)> c3b(static_cast<CF>(c1));
>+  VERIFY( c3b(c) == 1 );
>+
>+  std::move_only_function<int(CountedArg)> m3a(c1);
>+  VERIFY( m3a(c) == 1 );
>+
>+  std::move_only_function<int(CountedArg)> m3b(static_cast<CF>(c1));
>+  VERIFY( m3b(c) == 1 );
>+
>+  // Invokers internally uses Counted&& for non-trivial types,
>+  // sinature remain compatible.
>+  std::copyable_function<int(CountedArg&&)> c4a(c1);
>+  VERIFY( c4a({}) == 0 );
>+
>+  std::copyable_function<int(CountedArg&&)> c4b(static_cast<CF>(c1));
>+  VERIFY( c4b({}) == 0 );
>+
>+  std::move_only_function<int(CountedArg&&)> m4a(c1);
>+  VERIFY( m4a({}) == 0 );
>+
>+  std::move_only_function<int(CountedArg&&)> m4b(static_cast<CF>(c1));
>+  VERIFY( m4b({}) == 0 );
>+
>+  std::copyable_function<int(CountedArg&&)&> c5a(c1);
>+  VERIFY( c5a({}) == 0 );
>+
>+  std::copyable_function<int(CountedArg&&)&&> c5b(static_cast<CF>(c1));
>+  VERIFY( std::move(c5b)({}) == 0 );
>+
>+  std::move_only_function<int(CountedArg&&)&> m5a(c1);
>+  VERIFY( m5a({}) == 0 );
>+
>+  std::move_only_function<int(CountedArg&&)&&> m5b(static_cast<CF>(c1));
>+  VERIFY( std::move(m5b)({}) == 0 );
>+
>+  // Incompatible signatures
>+  std::copyable_function<long(CountedArg) const noexcept> c6a(c1);
>+  VERIFY( c6a(c) == 2 );
>+
>+  std::copyable_function<long(CountedArg) const noexcept> c6b(static_cast<CF>(c1));
>+  VERIFY( c6b(c) == 2 );
>+
>+  std::move_only_function<long(CountedArg) const noexcept> m6a(c1);
>+  VERIFY( m6a(c) == 2 );
>+
>+  std::move_only_function<long(CountedArg) const noexcept> m6b(static_cast<CF>(c1));
>+  VERIFY( m6b(c) == 2 );
>+}
>+
>+void
>+test02()
>+{
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::copyable_function<int(CountedArg) const noexcept> c1(f);
>+  using CF = std::copyable_function<int(CountedArg) const noexcept>;
>+  VERIFY( c1(c) == 1 );
>+
>+  std::copyable_function<int(CountedArg) const> c2;
>+  c2 = c1;
>+  VERIFY( c2(c) == 1 );
>+  c2 = static_cast<CF>(c1);
>+  VERIFY( c2(c) == 1 );
>+
>+  std::move_only_function<int(CountedArg) const> m2;
>+  m2 = c1;
>+  VERIFY( m2(c) == 1 );
>+  m2 = static_cast<CF>(c1);
>+  VERIFY( m2(c) == 1 );
>+
>+  // Incompatible signatures
>+  std::copyable_function<long(CountedArg) const noexcept> c3;
>+  c3 = c1;
>+  VERIFY( c3(c) == 2 );
>+  c3 = static_cast<CF>(c1);
>+  VERIFY( c3(c) == 2 );
>+
>+  std::move_only_function<long(CountedArg) const noexcept> m3;
>+  m3 = c1;
>+  VERIFY( m3(c) == 2 );
>+  m3 = static_cast<CF>(c1);
>+  VERIFY( m3(c) == 2 );
>+}
>+
>+void
>+test03()
>+{
>+  std::copyable_function<int(long) const noexcept> c1;
>+  VERIFY( c1 == nullptr );
>+
>+  std::copyable_function<int(long) const> c2(c1);
>+  VERIFY( c2 == nullptr );
>+  c2 = c1;
>+  VERIFY( c2 == nullptr );
>+  c2 = std::move(c1);
>+  VERIFY( c2 == nullptr );
>+
>+  std::copyable_function<bool(int) const> c3(std::move(c1));
>+  VERIFY( c3 == nullptr );
>+  c3 = c1;
>+  VERIFY( c3 == nullptr );
>+  c3 = std::move(c1);
>+  VERIFY( c3 == nullptr );
>+
>+  // LWG4255 move_only_function constructor should recognize empty
>+  //         copyable_functions
>+  std::move_only_function<int(long) const noexcept> m1(c1);
>+  VERIFY( m1 == nullptr );
>+  m1 = c1;
>+  VERIFY( m1 == nullptr );
>+  m1 = std::move(c1);
>+  VERIFY( m1 == nullptr );
>+
>+  std::move_only_function<int(long) const> m2(c1);
>+  VERIFY( m2 == nullptr );
>+  m2 = c1;
>+  VERIFY( m2 == nullptr );
>+  m2 = std::move(c1);
>+  VERIFY( m2 == nullptr );
>+
>+  std::move_only_function<bool(int) const> m3(std::move(c1));
>+  VERIFY( m3 == nullptr );
>+  m3 = c1;
>+  VERIFY( m3 == nullptr );
>+  m3 = std::move(c1);
>+  VERIFY( m3 == nullptr );
>+}
>+
>+void
>+test04()
>+{
>+  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::copyable_function<int(CountedArg) const> c1(f);
>+  VERIFY( c1(c) == 1001 );
>+
>+  // Call const overload as std::copyable_function<int(CountedArg) const>
>+  // inside td::copyable_function<int(CountedArg)> would do.
>+  std::copyable_function<int(CountedArg)> c2(c1);
>+  VERIFY( c2(c) == 1001 );
>+  std::move_only_function<int(CountedArg)> m2(c1);
>+  VERIFY( m2(c) == 1001 );
>+
>+  std::copyable_function<int(CountedArg)> m3(f);
>+  VERIFY( m3(c) == 1 );
>+}
>+
>+void
>+test05()
>+{
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::copyable_function<int(CountedArg)> w1(f);
>+  // copyable_function stores copyable_function due incompatibile signatures
>+  std::copyable_function<int(CountedArg const&)> w2(std::move(w1));
>+  // copy is made when passing to int(CountedArg)
>+  VERIFY( w2(c) == 1 );
>+  // wrapped 3 times
>+  w1 = std::move(w2);
>+  VERIFY( w1(c) == 2 );
>+  // wrapped 4 times
>+  w2 = std::move(w1);
>+  VERIFY( w2(c) == 2 );
>+  // wrapped 5 times
>+  w1 = std::move(w2);
>+  VERIFY( w1(c) == 3 );
>+}
>+
>+void
>+test06()
>+{
>+  // No special interoperability with std::function
>+  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
>+  std::function<int(CountedArg)> f1(f);
>+  std::copyable_function<int(CountedArg) const> c1(std::move(f1));
>+  VERIFY( c1(c) == 2 );
>+
>+  std::copyable_function<int(CountedArg) const> c2(f);
>+  std::function<int(CountedArg)> f2(c2);
>+  VERIFY( f2(c) == 2 );
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+  test04();
>+  test05();
>+  test06();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc
>new file mode 100644
>index 00000000000..6445a272b79
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc
>@@ -0,0 +1,154 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-effective-target hosted }
>+
>+#include <functional>
>+#include <testsuite_hooks.h>
>+
>+using std::copyable_function;
>+
>+void
>+test01()
>+{
>+  // Small type with non-throwing move constructor. Not allocated on the heap.
>+  struct F
>+  {
>+    F() = default;
>+    F(const F& f) : counters(f.counters) { ++counters.copy; }
>+    F(F&& f) noexcept : counters(f.counters) { ++counters.move; }
>+
>+    F& operator=(F&&) = delete;
>+
>+    struct Counters
>+    {
>+      int copy = 0;
>+      int move = 0;
>+    } counters;
>+
>+    const Counters& operator()() const { return counters; }
>+  };
>+
>+  F f;
>+  std::copyable_function<const F::Counters&() const> m1(f);
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  // This will copy construct a new target object
>+  auto m2 = m1;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m2().copy == 2 );
>+  VERIFY( m2().move == 0 );
>+
>+  m1 = m2;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m1().copy == 3 );
>+  VERIFY( m1().move == 1 ); // Copies object first and then swaps
>+
>+  m1 = m1;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m1().copy == 4 );
>+  VERIFY( m1().move == 2 ); // Copies object first and then swaps
>+
>+  m2 = f;
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( m2().move == 1 ); // Copy construct target object, then swap into m2.
>+}
>+
>+void
>+test02()
>+{
>+  // Move constructor is potentially throwing. Allocated on the heap.
>+  struct F
>+  {
>+    F() = default;
>+    F(const F& f) noexcept : counters(f.counters) { ++counters.copy; }
>+    F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; }
>+
>+    F& operator=(F&&) = delete;
>+
>+    struct Counters
>+    {
>+      int copy = 0;
>+      int move = 0;
>+    } counters;
>+
>+    Counters operator()() const noexcept { return counters; }
>+  };
>+
>+  F f;
>+  std::copyable_function<F::Counters() const> m1(f);
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  // The target object is on the heap, but we need to allocate new one
>+  auto m2 = m1;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m2().copy == 2 );
>+  VERIFY( m2().move == 0 );
>+
>+  m1 = m2;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m1().copy == 3 );
>+  VERIFY( m1().move == 0 );
>+
>+  m1 = m1;
>+  VERIFY( m1 != nullptr && m2 != nullptr );
>+  VERIFY( m1().copy == 4 );
>+  VERIFY( m1().move == 0 );
>+
>+  m2 = f;
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( m2().move == 0 );
>+}
>+
>+void
>+test03()
>+{
>+  // Small type with non-throwing, but not non-trivial move constructor.
>+  struct F
>+  {
>+    F(int i) noexcept : id(i) {}
>+    F(const F& f) : id(f.id)
>+    { if (id == 3) throw id; }
>+    F(F&& f) noexcept : id(f.id) {  }
>+
>+    int operator()() const
>+    { return id; }
>+
>+    int id;
>+  };
>+
>+  std::copyable_function<int() const> m1(std::in_place_type<F>, 1);
>+  const std::copyable_function<int() const> m2(std::in_place_type<F>, 2);
>+  const std::copyable_function<int() const> m3(std::in_place_type<F>, 3);
>+
>+  try
>+  {
>+    auto mc = m3;
>+    VERIFY( false );
>+  }
>+  catch(int i)
>+  {
>+    VERIFY( i == 3 );
>+  }
>+
>+  m1 = m2;
>+  VERIFY( m1() == 2 );
>+
>+  try
>+  {
>+    m1 = m3;
>+    VERIFY( false );
>+  }
>+  catch (int i)
>+  {
>+    VERIFY( i == 3 );
>+  }
>+  VERIFY( m1() == 2 );
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+}
>diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/move.cc b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc
>new file mode 100644
>index 00000000000..ec9d0d1af92
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc
>@@ -0,0 +1,120 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-effective-target hosted }
>+
>+#include <functional>
>+#include <testsuite_hooks.h>
>+
>+using std::copyable_function;
>+
>+void
>+test01()
>+{
>+  // Small type with non-throwing move constructor. Not allocated on the heap.
>+  struct F
>+  {
>+    F() = default;
>+    F(const F& f) : counters(f.counters) { ++counters.copy; }
>+    F(F&& f) noexcept : counters(f.counters) { ++counters.move; }
>+
>+    F& operator=(F&&) = delete;
>+
>+    struct Counters
>+    {
>+      int copy = 0;
>+      int move = 0;
>+    } counters;
>+
>+    const Counters& operator()() const { return counters; }
>+  };
>+
>+  F f;
>+  std::copyable_function<const F::Counters&() const> m1(f);
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  // Standard specifies move assigment as copy and swap
>+  m1 = std::move(m1);
>+  VERIFY( m1 != nullptr );
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  // This will move construct a new target object and destroy the old one:
>+  auto m2 = std::move(m1);
>+  VERIFY( m1 == nullptr && m2 != nullptr );
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( m2().move == 1 );
>+
>+  m1 = std::move(m2);
>+  VERIFY( m1 != nullptr && m2 == nullptr );
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 2 );
>+
>+  m2 = std::move(f);
>+  VERIFY( m2().copy == 0 );
>+  VERIFY( m2().move == 2 ); // Move construct target object, then swap into m2.
>+  const int moves = m1().move + m2().move;
>+  // This will do three moves:
>+  swap(m1, m2);
>+  VERIFY( m1().copy == 0 );
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( (m1().move + m2().move) == (moves + 3) );
>+}
>+
>+void
>+test02()
>+{
>+  // Move constructor is potentially throwing. Allocated on the heap.
>+  struct F
>+  {
>+    F() = default;
>+    F(const F& f) noexcept : counters(f.counters) { ++counters.copy; }
>+    F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; }
>+
>+    F& operator=(F&&) = delete;
>+
>+    struct Counters
>+    {
>+      int copy = 0;
>+      int move = 0;
>+    } counters;
>+
>+    Counters operator()() const noexcept { return counters; }
>+  };
>+
>+  F f;
>+  std::copyable_function<F::Counters() const> m1(f);
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  m1 = std::move(m1);
>+  VERIFY( m1 != nullptr );
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  // The target object is on the heap so this just moves a pointer:
>+  auto m2 = std::move(m1);
>+  VERIFY( m1 == nullptr && m2 != nullptr );
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( m2().move == 0 );
>+
>+  m1 = std::move(m2);
>+  VERIFY( m1 != nullptr && m2 == nullptr );
>+  VERIFY( m1().copy == 1 );
>+  VERIFY( m1().move == 0 );
>+
>+  m2 = std::move(f);
>+  VERIFY( m2().copy == 0 );
>+  VERIFY( m2().move == 1 );
>+  const int moves = m1().move + m2().move;
>+  // This just swaps the pointers, so no moves:
>+  swap(m1, m2);
>+  VERIFY( m1().copy == 0 );
>+  VERIFY( m2().copy == 1 );
>+  VERIFY( (m1().move + m2().move) == moves );
>+}
>+
>+int main()
>+{
>+  test01();
>+  test02();
>+}
>-- 
>2.49.0
>
>



More information about the Libstdc++ mailing list