On Wed, May 14, 2025 at 1:06 PM Jonathan Wakely <jwak...@redhat.com> 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 > > > > > >