On Mon, 12 May 2025, 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.
>       * 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.
> ---
>  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      | 268 ++++++++++++++++++
>  libstdc++-v3/include/bits/mofunc_impl.h       |   4 +
>  .../include/bits/move_only_function.h         |  91 +++++-
>  libstdc++-v3/include/bits/version.def         |  10 +
>  libstdc++-v3/include/bits/version.h           |  10 +
>  libstdc++-v3/include/std/functional           |   1 +
>  .../20_util/copyable_function/call.cc         | 224 +++++++++++++++
>  .../20_util/copyable_function/cons.cc         | 126 ++++++++
>  .../20_util/copyable_function/conv.cc         | 253 +++++++++++++++++
>  .../20_util/copyable_function/copy.cc         | 154 ++++++++++
>  .../20_util/copyable_function/move.cc         | 120 ++++++++
>  14 files changed, 1260 insertions(+), 4 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..c2840032494
> --- /dev/null
> +++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
> @@ -0,0 +1,268 @@
> +// 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 function wrapper.
> +   *  @ingroup functors
> +   *  @since C++23
> +   *  @headerfile functional
> +   *
> +   *  The `std::copyable_function` class template is a call wrapper similar
> +   *  to `std::function`, but does not require the stored target function
> +   *  to be copyable.
> +   *
> +   *  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

Shall we export std::copyable_function from the std module in this same
commit?

> +    {
> +      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.mov.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;
> +         }

I'd recommend an extra newline between this if and the next one to make
it clear that they're not related, and that there's no missing 'else'.

> +       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]]
> +       {
> +         _Base::operator=(static_cast<_Base&&>(__x));
> +         _M_invoke = std::__exchange(__x._M_invoke, nullptr);
> +       }
> +     return *this;
> +      }
> +
> +      /// Stores a copy of 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;

Some indentation inconsistencies in these friend declarations

> +    };
> +
> +#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 839f19e0389..68264bd9547 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 5eb688a0ef4..416b0a17ed6 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
>  
>  #include <bits/invoke.h>
>  #include <bits/utility.h>
> @@ -148,7 +148,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 { }
> @@ -356,6 +356,10 @@ _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
>     };
>  
>     template<typename _Func>
> @@ -381,6 +385,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  } // namespace __polyfunc
>    /// @endcond
>  
> +#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED
>    template<typename... _Signature>
>      class move_only_function; // not defined
>  
> @@ -400,10 +405,71 @@ _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)

Newline after 'void'

LGTM besides that

> +      {
> +     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"
> @@ -417,6 +483,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/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..0f1b163a56a
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> @@ -0,0 +1,253 @@
> +// { 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 );
> +
> +  // Standard specifies this as move_only_function containing
> +  // empty copyable_function, i.e. they are not equal nullptr.
> +  // Invoking call operator remains UB, however we cannot 
> +  // is UB, but it cannot be checked on user side.
> +  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
> 
> 

Reply via email to