On Wed, May 14, 2025 at 1:06 PM Jonathan Wakely <[email protected]> wrote:
> 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?
>
Went for using defined(__glibcxx_copyable_function) ||
defined(__glibcxx_copyable_function)
>
>
> >
> > #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!
>
It was removal of trailing whitespace. Locally removed it in previous
commit,
so I no longer see diff here.
>
> 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
> >
> >
>
>