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 also check __glibcxx_copyable_function.
(_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.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
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";
${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 \
@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 \
--- /dev/null
+// 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 it's
+ * 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 != std::addressof(__x)) [[likely]]
+ {
+ _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
&& (!__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>)
if (__f == nullptr)
return;
}
+
if constexpr (__is_polymorphic_function_v<_Vt>
&& __polyfunc::__is_invoker_convertible<_Vt, move_only_function>())
{
-// Implementation of std::move_only_function -*- C++ -*-
+// Implementation of std::move_only_function and std::copyable_function -*- C++ -*-
// Copyright The GNU Toolchain Authors.
//
#include <bits/version.h>
-#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED
+#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function)
#include <bits/invoke.h>
#include <bits/utility.h>
};
// 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 { }
{ _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
{ };
} // 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"
#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 // __glibcxx_copyable_function || __glibcxx_copyable_function
#endif // _GLIBCXX_MOVE_ONLY_FUNCTION_H
};
};
+ftms = {
+ name = copyable_function;
+ values = {
+ v = 202306;
+ cxxmin = 26;
+ hosted = yes;
+ };
+};
+
ftms = {
name = out_ptr;
values = {
#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
#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
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;
--- /dev/null
+// { 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();
+}
--- /dev/null
+// { 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);
+}
--- /dev/null
+// { 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();
+}
--- /dev/null
+// { 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();
+}
--- /dev/null
+// { 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();
+}