I noticed that I am missing deduction guides for function_ref.
Interestingly only function_ref has them, and not move_only_function,
copyable_function.

On Thu, May 15, 2025 at 5:00 AM Patrick Palka <ppa...@redhat.com> wrote:

>
>
> On Wed, 14 May 2025, Tomasz Kamiński wrote:
>
> > This patch implements C++26 function_ref as specified in P0792R14,
> > with correction for constraints for constructor accepting nontype_t
> > parameter from LWG 4256.
> >
> > As function_ref may store a pointer to the const object, __Ptrs::_M_obj
> is
> > changed to const void*, so again we do not cast away const from const
> > objects. To help with necessary cast, a __polyfunc::__cast_to helper is
> > added, that accepts a reference to that type.
> >
> > The _Invoker now defines additional call methods used by function_ref:
> > _S_ptrs() for invoking target passed by reference, and __S_nttp,
> _S_bind_ptr,
> > _S_bind_ref for handling constructors accepting nontype_t. The existing
> > _S_call_storage is changed to thin wrappers, that initialies _Ptrs,
> > and forwards to _S_call_ptrs.
> >
> > This reduced the most uses of _Storage::_M_ptr and _Storage::_M_ref,
> > so this functions was removed, and _Manager uses were adjusted.
> >
> > Finally we make function_ref available in freestanding mode, as
> > move_only_function and copyable_function iarecurrently only available in
> hosted,
>
> "are currently"
>
> > so we define _Manager and _Mo_base only if either
> __glibcxx_move_only_function
> > or __glibcxx_copyable_function is defined.
> >
> >       PR libstdc++/119126
> >
> > libstdc++-v3/ChangeLog:
> >
> >       * doc/doxygen/stdheader.cc: Added funcref_impl.h file.
> >       * include/Makefile.am: Added funcref_impl.h file.
> >       * include/Makefile.in: Added funcref_impl.h file.
> >       * include/bits/funcref_impl.h: New file.
> >       * include/bits/funcwrap.h: (_Ptrs::_M_obj): Const-qualify.
> >       (_Storage::_M_ptr, _Storage::_M_ref): Remove.
> >       (__polyfunc::__cast_to) Define.
> >       (_Base_invoker::_S_ptrs, _Base_invoker::_S_nttp)
> >       (_Base_invoker::_S_bind_ptrs, _Base_invoker::_S_bind_ref)
> >       (_Base_invoker::_S_call_ptrs): Define.
> >       (_Base_invoker::_S_call_storage): Foward to _S_call_ptrs.
> >       (_Manager::_S_local, _Manager::_S_ptr): Adjust for _M_obj being
> >       const qualified.
> >       (__polyfunc::_Manager, __polyfunc::_Mo_base): Guard with
> >       __glibcxx_move_only_function || __glibcxx_copyable_function.
> >       (std::function_ref, std::__is_function_ref_v)
> >       [__glibcxx_function_ref]: Define.
> >       * include/bits/utility.h (std::nontype_t, std::nontype)
> >       (__is_nontype_v) [__glibcxx_function_ref]: Define.
> >       * include/bits/version.def: Define function_ref.
> >       * include/bits/version.h: Regenerate.
> >       * src/c++23/std.cc.in (std::function_ref)
> [__cpp_lib_function_ref]:
> >        Export.
> >       * testsuite/20_util/function_ref/assign.cc: New test.
> >       * testsuite/20_util/function_ref/call.cc: New test.
> >       * testsuite/20_util/function_ref/cons.cc: New test.
> >       * testsuite/20_util/function_ref/cons_neg.cc: New test.
> >       * testsuite/20_util/function_ref/conv.cc: New test.
>
> Should some of these tests run in freestanding mode too, given that
> function_ref is freestanding?

I have made all test expect conv.cc to run in freestanding mode.
The conv.cc uses move_only_function and copyable_function, so I left
requirement for it to be hosted.

>
> > ---
> > Would appreciate check of the documentation comments in funcref_impl.h
> > file.
> >
> >  libstdc++-v3/doc/doxygen/stdheader.cc         |   1 +
> >  libstdc++-v3/include/Makefile.am              |   1 +
> >  libstdc++-v3/include/Makefile.in              |   1 +
> >  libstdc++-v3/include/bits/funcref_impl.h      | 185 +++++++++++++++
> >  libstdc++-v3/include/bits/funcwrap.h          | 154 ++++++++----
> >  libstdc++-v3/include/bits/utility.h           |  17 ++
> >  libstdc++-v3/include/bits/version.def         |   8 +
> >  libstdc++-v3/include/bits/version.h           |  10 +
> >  libstdc++-v3/include/std/functional           |   4 +-
> >  libstdc++-v3/src/c++23/std.cc.in              |   3 +
> >  .../testsuite/20_util/function_ref/assign.cc  | 110 +++++++++
> >  .../testsuite/20_util/function_ref/call.cc    | 145 ++++++++++++
> >  .../testsuite/20_util/function_ref/cons.cc    | 219 ++++++++++++++++++
> >  .../20_util/function_ref/cons_neg.cc          |  30 +++
> >  .../testsuite/20_util/function_ref/conv.cc    | 152 ++++++++++++
> >  15 files changed, 993 insertions(+), 47 deletions(-)
> >  create mode 100644 libstdc++-v3/include/bits/funcref_impl.h
> >  create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/assign.cc
> >  create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/call.cc
> >  create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> >  create mode 100644
> libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
> >  create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> >
> > diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc
> b/libstdc++-v3/doc/doxygen/stdheader.cc
> > index 839bfc81bc0..938b2b04a26 100644
> > --- a/libstdc++-v3/doc/doxygen/stdheader.cc
> > +++ b/libstdc++-v3/doc/doxygen/stdheader.cc
> > @@ -55,6 +55,7 @@ void init_map()
> >      headers["functional_hash.h"]        = "functional";
> >      headers["mofunc_impl.h"]            = "functional";
> >      headers["cpyfunc_impl.h"]           = "functional";
> > +    headers["funcref_impl.h"]           = "functional";
> >      headers["funcwrap.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 3e5b6c4142e..baf0290d655 100644
> > --- a/libstdc++-v3/include/Makefile.am
> > +++ b/libstdc++-v3/include/Makefile.am
> > @@ -205,6 +205,7 @@ bits_headers = \
> >       ${bits_srcdir}/fs_ops.h \
> >       ${bits_srcdir}/fs_path.h \
> >       ${bits_srcdir}/fstream.tcc \
> > +     ${bits_srcdir}/funcref_impl.h \
> >       ${bits_srcdir}/funcwrap.h \
> >       ${bits_srcdir}/gslice.h \
> >       ${bits_srcdir}/gslice_array.h \
> > diff --git a/libstdc++-v3/include/Makefile.in
> b/libstdc++-v3/include/Makefile.in
> > index 3531162b5f7..e4e1079d8bd 100644
> > --- a/libstdc++-v3/include/Makefile.in
> > +++ b/libstdc++-v3/include/Makefile.in
> > @@ -559,6 +559,7 @@ bits_freestanding = \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/fs_path.h \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/fstream.tcc \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/funcwrap.h \
> > +@GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/funcref_impl.h \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/gslice.h \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/gslice_array.h \
> >  @GLIBCXX_HOSTED_TRUE@        ${bits_srcdir}/hashtable.h \
> > diff --git a/libstdc++-v3/include/bits/funcref_impl.h
> b/libstdc++-v3/include/bits/funcref_impl.h
> > new file mode 100644
> > index 00000000000..1b1c020d0f9
> > --- /dev/null
> > +++ b/libstdc++-v3/include/bits/funcref_impl.h
> > @@ -0,0 +1,185 @@
> > +// Implementation of std::function_ref -*- 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/funcref_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
>
> Seems the macro _GLIBCXX_MOF_CV_REF is unused, and _GLIBCXX_MOF_REF is
> always empty for function_ref.
>
I have removed all these macros except  _GLIBCXX_MOF_CV.

>
> > +
> > +namespace std _GLIBCXX_VISIBILITY(default)
> > +{
> > +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> > +
> > +  /**
> > +   *  @brief Non-owning polymorphic function wrapper.
> > +   *  @ingroup functors
> > +   *  @since C++26
> > +   *  @headerfile functional
> > +   *
> > +   *  The `std::function_ref` class template is a non-owning call
> wrapper,
> > +   *  that refers a bound object. Using function_ref outside of lifetime
> > +   *  of bound object has undefined behavior.
> > +   *
> > +   *  It supports const-qualifion and no-throw guarantees. The
> qualifications and
>
> const-qualification
>
> > +   *  exception-specification of the signature are respected when when
> invoking the
> > +   *  reference function.
> > +   */
> > +  template<typename _Res, typename... _ArgTypes, bool _Noex>
> > +    class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
> > +                          noexcept(_Noex)>
> > +    {
> > +      using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
> > +      using _Signature = _Invoker::_Signature;
> > +
> > +      // [func.wrap.ref.ctor]/1 is-invokable-using
> > +      template<typename... _Tps>
> > +     static constexpr bool __is_invocable_using
> > +       = __conditional_t<_Noex,
> > +                         is_nothrow_invocable_r<_Res, _Tps...,
> _ArgTypes...>,
> > +                         is_invocable_r<_Res, _Tps...,
> _ArgTypes...>>::value;
> > +
> > +    public:
> > +      /// Target and bound object is function pointed by parameter.
> > +      template<typename _Fn>
> > +     requires is_function_v<_Fn> && __is_invocable_using<_Fn*>
> > +     function_ref(_Fn* __fn) noexcept
> > +     {
> > +       __glibcxx_assert(__fn != nullptr);
> > +       this->_M_invoke = _Invoker::template _S_ptrs<_Fn*>();
> > +       this->_M_ptrs._M_func = reinterpret_cast<void(*)()>(__fn);
> > +     }
> > +
> > +      /// Targed and bound object referenced by parameter.
>
> Target
>
> > +      template<typename _Fn, typename _Vt = remove_reference_t<_Fn>>
> > +     requires (!is_same_v<remove_cv_t<_Vt>, function_ref>)
> > +            && (!is_member_pointer_v<_Vt>)
> > +            // We deviate from standard by having this condition, that
> forces
> > +            // function references to use _Fn* constructors. This
> simplies
> > +            // implementation and provide better diagnostic when used in
> > +            // constant expression (above constructor is not constexpr).
> > +            && (!is_function_v<_Vt>)
> > +            && __is_invocable_using<_Vt _GLIBCXX_MOF_INV_QUALS>
> > +     constexpr
> > +     function_ref(_Fn&& __f) noexcept
> > +     {
> > +       this->_M_invoke = _Invoker::template _S_ptrs<_Vt
> _GLIBCXX_MOF_INV_QUALS>();
> > +       this->_M_ptrs._M_obj = std::addressof(__f);
> > +     }
> > +
> > +      // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > +      // 4256. Incorrect constrains for function_ref constructors from
> nontype
> > +      /// Target object is __fn. There is no bound object.
> > +      template<auto __fn>
> > +     requires __is_invocable_using<const decltype(__fn)&>
> > +     constexpr
> > +     function_ref(nontype_t<__fn>) noexcept
> > +     {
> > +       using _Fn = remove_cv_t<decltype(__fn)>;
> > +       if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
> > +         static_assert(__fn != nullptr);
> > +
> > +       this->_M_invoke = &_Invoker::template _S_nttp<__fn>;
> > +       this->_M_ptrs._M_obj = nullptr;
> > +     }
> > +
> > +      /// Target object is equivalent to
> std::bind_front<_fn>(std::ref(__ref)).
> > +      /// Bound object is object referenced by second parameters.
>
> parameter
>
> > +      template<auto __fn, typename _Up, typename _Td =
> remove_reference_t<_Up>>
> > +     requires (!is_rvalue_reference_v<_Up&&>)
> > +       && __is_invocable_using<const decltype(__fn)&, _Td
> _GLIBCXX_MOF_INV_QUALS>
> > +     constexpr
> > +     function_ref(nontype_t<__fn>, _Up&& __ref) noexcept
> > +     {
> > +       using _Fn = remove_cv_t<decltype(__fn)>;
> > +       if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
> > +         static_assert(__fn != nullptr);
> > +
> > +       using _Tr = _Td _GLIBCXX_MOF_INV_QUALS;
> > +       if constexpr (is_member_pointer_v<_Fn> &&
> is_lvalue_reference_v<_Tr>)
> > +         // N.B. invoking member pointer on lvalue produces the same
> effects,
> > +         // as invoking it on pointer to that lvalue.
> > +         this->_M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td
> _GLIBCXX_MOF_CV>;
> > +       else
> > +         this->_M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>;
> > +       this->_M_ptrs._M_obj = std::addressof(__ref);
> > +     }
> > +
> > +      /// Target object is equivalent to std::bind_front<_fn>(__ptr).
> > +      /// Bound object is object pointed by second parameter (if any).
> > +      template<auto __fn, typename _Td>
> > +     requires __is_invocable_using<const decltype(__fn)&, _Td
> _GLIBCXX_MOF_CV*>
> > +     constexpr
> > +     function_ref(nontype_t<__fn>, _Td _GLIBCXX_MOF_CV* __ptr) noexcept
> > +     {
> > +       using _Fn = remove_cv_t<decltype(__fn)>;
> > +       if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
> > +         static_assert(__fn != nullptr);
> > +       if constexpr (is_member_pointer_v<_Fn>)
> > +         __glibcxx_assert(__ptr != nullptr);
> > +
> > +       this->_M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td
> _GLIBCXX_MOF_CV>;
> > +       this->_M_ptrs._M_obj = __ptr;
> > +     }
> > +
> > +      template<typename _Tp>
> > +     requires (!is_same_v<_Tp, function_ref>)
> > +            && (!is_pointer_v<_Tp>) && (!__is_nontype_v<_Tp>)
> > +     function_ref&
> > +     operator=(_Tp) = delete;
> > +
> > +      /** Invoke the target object.
> > +       *
> > +       * The bound object will be invoked using the supplied arguments,
> > +       * and as const or non-const, as dictated by the template
> arguments
> > +       * of the `function_ref` specialization.
> > +       */
> > +      _Res
> > +      operator()(_ArgTypes... __args) const noexcept(_Noex)
> > +      {      return _M_invoke(this->_M_ptrs,
> std::forward<_ArgTypes>(__args)...); }
>
> Stray tab
>
> > +
> > +    private:
> > +      typename _Invoker::__ptrs_func_t _M_invoke;
> > +      __polyfunc::_Ptrs _M_ptrs;
> > +    };
> > +
> > +#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/funcwrap.h
> b/libstdc++-v3/include/bits/funcwrap.h
> > index 4e053534cf5..6785da8fcce 100644
> > --- a/libstdc++-v3/include/bits/funcwrap.h
> > +++ b/libstdc++-v3/include/bits/funcwrap.h
> > @@ -1,4 +1,5 @@
> > -// Implementation of std::move_only_function and std::copyable_function
> -*- C++ -*-
> > +// Implementation of std::move_only_function, std::copyable_function
> > +// and std::function_ref  -*- C++ -*-
> >
> >  // Copyright The GNU Toolchain Authors.
> >  //
> > @@ -36,7 +37,8 @@
> >
> >  #include <bits/version.h>
> >
> > -#if defined(__glibcxx_move_only_function) ||
> defined(__glibcxx_copyable_function)
> > +#if defined(__glibcxx_move_only_function) \
> > +   || defined(__glibcxx_copyable_function) ||
> defined(__glibcxx_function_ref)
> >
> >  #include <bits/invoke.h>
> >  #include <bits/utility.h>
> > @@ -53,10 +55,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >    {
> >      union _Ptrs
> >      {
> > -      void* _M_obj;
> > +      const void* _M_obj;
> >        void (*_M_func)();
> >      };
> >
> > +   template<typename _Tp>
> > +     [[__gnu__::__always_inline__]]
> > +     constexpr auto
> > +     __cast_to(_Ptrs __ptrs) noexcept
>
> Maybe use auto* to make it obvious this returns a pointer?
>
Good idea, changee it.

>
> > +     {
> > +       using _Td = remove_reference_t<_Tp>;
> > +       if constexpr (is_const_v<_Td>)
> > +      return static_cast<_Td*>(__ptrs._M_obj);
> > +       else
> > +      return static_cast<_Td*>(const_cast<void*>(__ptrs._M_obj));
> > +     }
> > +
> >     struct _Storage
> >     {
> >       void*       _M_addr() noexcept       { return &_M_bytes[0]; }
> > @@ -97,32 +111,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >          ::new (_M_addr()) _Tp(std::forward<_Args>(__args)...);
> >         }
> >
> > -     template<typename _Tp>
> > -       [[__gnu__::__always_inline__]]
> > -       _Tp*
> > -       _M_ptr() const noexcept
> > -       {
> > -      if constexpr (!_S_stored_locally<remove_const_t<_Tp>>())
> > -        return static_cast<_Tp*>(_M_ptrs._M_obj);
> > -      else if constexpr (is_const_v<_Tp>)
> > -        return static_cast<_Tp*>(_M_addr());
> > -      else
> > -        // _Manager and _Invoker pass _Storage by const&, even for
> mutable sources.
> > -        return static_cast<_Tp*>(const_cast<void*>(_M_addr()));
> > -       }
> > -
> > -     template<typename _Ref>
> > -       [[__gnu__::__always_inline__]]
> > -       _Ref
> > -       _M_ref() const noexcept
> > -       {
> > -      using _Tp = remove_reference_t<_Ref>;
> > -      if constexpr (is_function_v<remove_pointer_t<_Tp>>)
> > -        return reinterpret_cast<_Tp>(_M_ptrs._M_func);
> > -      else
> > -        return static_cast<_Ref>(*_M_ptr<_Tp>());
> > -       }
> > -
> >       // We want to have enough space to store a simple delegate type.
> >       struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; };
> >       union {
> > @@ -143,6 +131,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        _S_storage()
> >        { return &_S_call_storage<_Adjust_target<_Tp>>; }
> >
> > +       using __ptrs_func_t = _Ret(*)(_Ptrs, _Args...) noexcept(_Noex);
> > +       template<typename _Tp>
> > +      static consteval __ptrs_func_t
> > +      _S_ptrs()
> > +      { return &_S_call_ptrs<_Adjust_target<_Tp>>; }
> > +
> > +#ifdef __glibcxx_function_ref // C++ >= 26
> > +       template<auto __fn>
> > +      static _Ret
> > +      _S_nttp(_Ptrs, _Args... __args) noexcept(_Noex)
> > +      { return std::__invoke_r<_Ret>(__fn,
> std::forward<_Args>(__args)...); }
> > +
> > +       template<auto __fn, typename _Tp>
> > +      static _Ret
> > +      _S_bind_ptr(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
> > +      {
> > +        auto __p = __polyfunc::__cast_to<_Tp>(__ptrs);
> > +        return std::__invoke_r<_Ret>(__fn, __p,
> std::forward<_Args>(__args)...);
> > +      }
> > +
> > +       template<auto __fn, typename _Ref>
> > +      static _Ret
> > +      _S_bind_ref(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
> > +      {
> > +        auto __p = __polyfunc::__cast_to<_Ref>(__ptrs);
> > +        return std::__invoke_r<_Ret>(__fn, static_cast<_Ref>(*__p),
> > +                                     std::forward<_Args>(__args)...);
> > +      }
> > +#endif // __glibcxx_function_ref
> > +
> >       private:
> >         template<typename _Tp, typename _Td = remove_cvref_t<_Tp>>
> >        using _Adjust_target =
> > @@ -152,8 +170,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        static _Ret
> >        _S_call_storage(const _Storage& __ref, _Args... __args)
> noexcept(_Noex)
> >        {
> > -        return std::__invoke_r<_Ret>(__ref._M_ref<_Tp>(),
> > -                                     std::forward<_Args>(__args)...);
> > +        _Ptrs __ptrs;
> > +        if constexpr (is_function_v<remove_pointer_t<_Tp>>)
> > +          __ptrs._M_func = __ref._M_ptrs._M_func;
> > +        else if constexpr
> (!_Storage::_S_stored_locally<remove_cvref_t<_Tp>>())
> > +          __ptrs._M_obj = __ref._M_ptrs._M_obj;
> > +        else
> > +          __ptrs._M_obj = __ref._M_addr();
> > +        return _S_call_ptrs<_Tp>(__ptrs,
> std::forward<_Args>(__args)...);
> > +      }
> > +
> > +       template<typename _Tp>
> > +      static _Ret
> > +      _S_call_ptrs(_Ptrs __ptrs, _Args... __args) noexcept(_Noex)
> > +      {
> > +        if constexpr (is_function_v<remove_pointer_t<_Tp>>)
> > +          return
> std::__invoke_r<_Ret>(reinterpret_cast<_Tp>(__ptrs._M_func),
> > +                                       std::forward<_Args>(__args)...);
> > +        else
> > +          {
> > +            auto __p = __polyfunc::__cast_to<_Tp>(__ptrs);
> > +            return std::__invoke_r<_Ret>(static_cast<_Tp>(*__p),
> > +
>  std::forward<_Args>(__args)...);
> > +          }
> >        }
> >       };
> >
> > @@ -184,6 +223,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        return false;
> >       }
> >
> > +#if defined(__glibcxx_move_only_function) ||
> defined(__glibcxx_copyable_function)
> >     struct _Manager
> >     {
> >       enum class _Op
> > @@ -241,7 +281,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         switch (__op)
> >         {
> >        case _Op::_Address:
> > -        __target._M_ptrs._M_obj = const_cast<void*>(__src->_M_addr());
> > +        __target._M_ptrs._M_obj = __src->_M_addr();
> >          return;
> >        case _Op::_Move:
> >        case _Op::_Copy:
> > @@ -263,24 +303,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        switch (__op)
> >        {
> >          case _Op::_Address:
> > -          __target._M_ptrs._M_obj = __src->_M_ptr<_Tp>();
> > +          __target._M_ptrs._M_obj = __src->_M_addr();
> >            return;
> >          case _Op::_Move:
> >            {
> > -            _Tp* __obj = __src->_M_ptr<_Tp>();
> > +            _Tp* __obj =
> static_cast<_Tp*>(const_cast<void*>(__src->_M_addr()));
> >              ::new(__target._M_addr()) _Tp(std::move(*__obj));
> >              __obj->~_Tp();
> >            }
> >            return;
> >          case _Op::_Destroy:
> > -          __target._M_ptr<_Tp>()->~_Tp();
> > +          static_cast<_Tp*>(__target._M_addr())->~_Tp();
> >            return;
> >          case _Op::_Copy:
> >            if constexpr (_Provide_copy)
> > -            ::new (__target._M_addr()) _Tp(__src->_M_ref<const _Tp&>());
> > -          else
> > -            __builtin_unreachable();
> > -          return;
> > +            {
> > +              auto* __obj = static_cast<const _Tp*>(__src->_M_addr());
> > +              ::new (__target._M_addr()) _Tp(*__obj);
> > +              return;
> > +            }
> > +          __builtin_unreachable();
> >        }
> >         }
> >
> > @@ -296,14 +338,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >            __target._M_ptrs._M_obj = __src->_M_ptrs._M_obj;
> >            return;
> >          case _Op::_Destroy:
> > -          delete __target._M_ptr<_Tp>();
> > +          delete static_cast<const _Tp*>(__target._M_ptrs._M_obj);
> >            return;
> >          case _Op::_Copy:
> >            if constexpr (_Provide_copy)
> > -            __target._M_ptrs._M_obj = new _Tp(__src->_M_ref<const
> _Tp&>());
> > -          else
> > -            __builtin_unreachable();
> > -          return;
> > +            {
> > +              auto* __obj = static_cast<const
> _Tp*>(__src->_M_ptrs._M_obj);
> > +              __target._M_ptrs._M_obj = new _Tp(*__obj);
> > +              return;
> > +            }
> > +          __builtin_unreachable();
> >         }
> >       }
> >     };
> > @@ -382,7 +426,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       friend class _Cpy_base;
> >  #endif // __glibcxx_copyable_function
> >     };
> > -
> > +#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
> >  } // namespace __polyfunc
> >    /// @endcond
> >
> > @@ -468,6 +512,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >    }  // namespace __detail::__variant
> >  #endif // __glibcxx_copyable_function
> >
> > +#ifdef __glibcxx_function_ref  // C++ >= 26
> > +  template<typename... _Signature>
> > +    class function_ref; // not defined
> > +
> > +   /// @cond undocumented
> > +   template<typename _Tp>
> > +     inline constexpr bool __is_function_ref_v = false;
> > +   template<typename _Tp>
> > +     inline constexpr bool __is_function_ref_v<function_ref<_Tp>> =
> true;
> > +   /// @endcond
>
> This variable template seems unused.
>
Indeed. I thought I would need this one for operator=() = delete,
but it is not needed:
https://eel.is/c++draft/function.objects#func.wrap.ref.ctor-21.1.

>
> > +#endif // __glibcxx_function_ref
> > +
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace std
> >
> > @@ -503,5 +559,11 @@ _GLIBCXX_END_NAMESPACE_VERSION
> >  #include "cpyfunc_impl.h"
> >  #endif // __glibcxx_copyable_function
> >
> > -#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
> > +#ifdef __glibcxx_function_ref  // C++ >= 26
> > +#include "funcref_impl.h"
> > +#define _GLIBCXX_MOF_CV const
> > +#include "funcref_impl.h"
> > +#endif // __glibcxx_function_ref
> > +
> > +#endif // move_only_function || copyable_function || function_ref
> >  #endif // _GLIBCXX_FUNCWRAP_H
> > diff --git a/libstdc++-v3/include/bits/utility.h
> b/libstdc++-v3/include/bits/utility.h
> > index 6fa6b679c6d..84d25e0c658 100644
> > --- a/libstdc++-v3/include/bits/utility.h
> > +++ b/libstdc++-v3/include/bits/utility.h
> > @@ -316,6 +316,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >    inline constexpr sorted_equivalent_t sorted_equivalent{};
> >  #endif
> >
> > +#if __glibcxx_function_ref // >= C++26
> > +  template<auto>
> > +    struct nontype_t
> > +    {
> > +      explicit nontype_t() = default;
> > +    };
> > +
> > +  template<auto __val>
> > +    constexpr nontype_t<__val> nontype{};
> > +
> > +  template<typename>
> > +    inline constexpr bool __is_nontype_v = false;
> > +
> > +  template<auto __val>
> > +    inline constexpr bool __is_nontype_v<nontype_t<__val>> = true;
> > +#endif
> > +
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace
> >
> > diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> > index 6ca148f0488..55aab40d9bf 100644
> > --- a/libstdc++-v3/include/bits/version.def
> > +++ b/libstdc++-v3/include/bits/version.def
> > @@ -1756,6 +1756,14 @@ ftms = {
> >    };
> >  };
> >
> > +ftms = {
> > +  name = function_ref;
> > +  values = {
> > +    v = 202306;
> > +    cxxmin = 26;
> > +  };
> > +};
> > +
> >  ftms = {
> >    name = out_ptr;
> >    values = {
> > diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> > index 48a090c14a3..026c9959482 100644
> > --- a/libstdc++-v3/include/bits/version.h
> > +++ b/libstdc++-v3/include/bits/version.h
> > @@ -1958,6 +1958,16 @@
> >  #endif /* !defined(__cpp_lib_copyable_function) &&
> defined(__glibcxx_want_copyable_function) */
> >  #undef __glibcxx_want_copyable_function
> >
> > +#if !defined(__cpp_lib_function_ref)
> > +# if (__cplusplus >  202302L)
> > +#  define __glibcxx_function_ref 202306L
> > +#  if defined(__glibcxx_want_all) ||
> defined(__glibcxx_want_function_ref)
> > +#   define __cpp_lib_function_ref 202306L
> > +#  endif
> > +# endif
> > +#endif /* !defined(__cpp_lib_function_ref) &&
> defined(__glibcxx_want_function_ref) */
> > +#undef __glibcxx_want_function_ref
> > +
> >  #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 9a55b181e6d..8431550e4ae 100644
> > --- a/libstdc++-v3/include/std/functional
> > +++ b/libstdc++-v3/include/std/functional
> > @@ -57,6 +57,7 @@
> >  #define __glibcxx_want_bind_back
> >  #define __glibcxx_want_constexpr_functional
> >  #define __glibcxx_want_copyable_function
> > +#define __glibcxx_want_function_ref
> >  #define __glibcxx_want_invoke
> >  #define __glibcxx_want_invoke_r
> >  #define __glibcxx_want_move_only_function
> > @@ -86,7 +87,8 @@
> >  # include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc.
> >  # include <compare>
> >  #endif
> > -#if defined(__glibcxx_move_only_function) ||
> defined(__glibcxx_copyable_function)
> > +#if defined(__glibcxx_move_only_function) \
> > +   || defined(__glibcxx_copyable_function) ||
> defined(__glibcxx_function_ref)
> >  # include <bits/funcwrap.h>
> >  #endif
> >
> > diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
> std.cc.in
> > index 417c8a1a562..af44ab5c617 100644
> > --- a/libstdc++-v3/src/c++23/std.cc.in
> > +++ b/libstdc++-v3/src/c++23/std.cc.in
> > @@ -1414,6 +1414,9 @@ export namespace std
> >  #endif
> >  #if __cpp_lib_copyable_function
> >    using std::copyable_function;
> > +#endif
> > +#if __cpp_lib_function_ref
> > +  using std::function_ref;
>
> Do we also need to export nontype_t and nontype?
>
> >  #endif
> >    using std::multiplies;
> >    using std::negate;
> > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
> > new file mode 100644
> > index 00000000000..9acc525aa48
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
> > @@ -0,0 +1,110 @@
> > +// { dg-do compile { target c++26 } }
> > +// { dg-require-effective-target hosted }
> > +// { dg-add-options no_pch }
> > +
> > +#include <functional>
> > +
> > +#ifndef __cpp_lib_function_ref
> > +# error "Feature-test macro for function_ref missing in <functional>"
> > +#elif __cpp_lib_function_ref != 202306L
> > +# error "Feature-test macro for function_ref has wrong value in
> <functional>"
> > +#endif
> > +
> > +using std::nontype;
> > +using std::nontype_t;
> > +using std::function_ref;
> > +
> > +using std::is_nothrow_move_assignable_v;
> > +using std::is_nothrow_copy_assignable_v;
> > +using std::is_nothrow_assignable_v;
> > +using std::is_assignable_v;
> > +using std::is_nothrow_swappable_v;
> > +using std::is_trivially_copyable_v;
> > +
> > +static_assert( is_nothrow_move_assignable_v<function_ref<void()>> );
> > +static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> );
> > +static_assert( is_nothrow_swappable_v<function_ref<void()>> );
> > +
> > +static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t>
> );
> > +
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()>
> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()>
> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()>
> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()>
> );
> > +static_assert( ! is_nothrow_assignable_v<function_ref<void()>,
> void(int)> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void(int)>,
> void(int)> );
> > +
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>,
> > +                                    void() noexcept> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>,
> > +                                     void() noexcept> );
> > +static_assert( ! is_assignable_v<function_ref<void() noexcept>, void()
> > );
> > +
> > +struct S
> > +{
> > +  int x;
> > +  int f();
> > +};
> > +int funS(S);
> > +
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    decltype(funS)> );
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    decltype(&funS)> );
> > +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)>
> );
> > +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)>
> );
> > +
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    nontype_t<funS>> );
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    nontype_t<&funS>> );
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    nontype_t<&S::x>> );
> > +static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
> > +                                    nontype_t<&S::f>> );
> > +struct Q
> > +{
> > +  void operator()() const;
> > +};
> > +
> > +static_assert( ! is_assignable_v<function_ref<void()>, Q> );
> > +static_assert( ! is_assignable_v<function_ref<void()>, Q&> );
> > +static_assert( ! is_assignable_v<function_ref<void()>, const Q&> );
> > +static_assert( ! is_assignable_v<function_ref<void() const>, Q> );
> > +static_assert( ! is_assignable_v<function_ref<void() const>, Q&> );
> > +static_assert( ! is_assignable_v<function_ref<void() const>, const Q&>
> );
> > +
> > +static_assert( is_nothrow_assignable_v<function_ref<void()>,
> > +                                    nontype_t<Q{}>> );
> > +static_assert( is_nothrow_assignable_v<function_ref<void() const>,
> > +                                    nontype_t<Q{}>> );
> > +
> > +constexpr bool
> > +test_constexpr()
> > +{
> > +  function_ref<void(S)> fp(nontype<funS>);
> > +  fp = nontype<funS>;
> > +  fp = nontype<&funS>;
> > +  fp = nontype<&S::x>;
> > +  fp = nontype<&S::f>;
> > +
> > +  constexpr Q cq;
> > +  function_ref<void() const> fq(cq);
> > +  fq = nontype<cq>;
> > +  return true;
> > +}
> > +static_assert( test_constexpr() );
> > +
> > +void func();
> > +
> > +void
> > +test_instantiation()
> > +{
> > +  function_ref<void(S)> fp(funS);
> > +  fp = funS;
> > +  fp = &funS;
> > +
> > +  test_constexpr();
> > +}
> > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> > new file mode 100644
> > index 00000000000..cf42aec3cbc
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> > @@ -0,0 +1,145 @@
> > +// { dg-do run { target c++26 } }
> > +// { dg-require-effective-target hosted }
> > +
> > +#include <functional>
> > +#include <utility>
> > +#include <testsuite_hooks.h>
> > +
> > +using std::nontype;
> > +using std::function_ref;
> > +
> > +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<function_ref<void()>>> );
> > +static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> );
> > +static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> );
> > +
> > +// Const qualier applies to target object
> > +static_assert( is_invocable_v< function_ref<void()> const > );
> > +static_assert( is_invocable_v< function_ref<void()> const &> );
> > +static_assert( is_invocable_v< function_ref<void() const> > );
> > +static_assert( is_invocable_v< function_ref<void() const> &> );
> > +static_assert( is_invocable_v< function_ref<void() const> const > );
> > +static_assert( is_invocable_v< function_ref<void() const> const &> );
> > +
> > +// With noexcept-specifier
> > +static_assert( ! is_nothrow_invocable_v< function_ref<void()> > );
> > +static_assert( ! is_nothrow_invocable_v< function_ref<void()
> noexcept(false)> > );
> > +static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> >
> );
> > +
> > +void
> > +test01()
> > +{
> > +  struct F
> > +  {
> > +    int operator()() { return 0; }
> > +    int operator()() const { return 1; }
> > +  };
> > +
> > +  function_ref<int()> f0{F{}};
> > +  VERIFY( f0() == 0 );
> > +  VERIFY( std::move(f0)() == 0 );
> > +
> > +  function_ref<int()> f1{nontype<F{}>};
> > +  VERIFY( f1() == 1 );
> > +  VERIFY( std::move(f1)() == 1 );
> > +
> > +  function_ref<int() const> f2{F{}};
> > +  VERIFY( f2() == 1 );
> > +  VERIFY( std::as_const(f2)() == 1 );
> > +  VERIFY( std::move(f2)() == 1 );
> > +  VERIFY( std::move(std::as_const(f2))() == 1 );
> > +
> > +  function_ref<int() const> f3{nontype<F{}>};
> > +  VERIFY( f3() == 1 );
> > +  VERIFY( std::as_const(f3)() == 1 );
> > +  VERIFY( std::move(f3)() == 1 );
> > +  VERIFY( std::move(std::as_const(f3))() == 1 );
> > +}
> > +
> > +void
> > +test02()
> > +{
> > +  struct F
> > +  {
> > +    struct Arg {};
> > +    int operator()(Arg& arg) const { return 0; }
> > +    int operator()(const Arg& arg) const { return 1; }
> > +  };
> > +  F::Arg arg;
> > +
> > +  function_ref<int()> f0{std::nontype<F{}>, arg};
> > +  VERIFY( f0() == 0 );
> > +  VERIFY( std::move(f0)() == 0 );
> > +
> > +  function_ref<int() const> f1{std::nontype<F{}>, arg};
> > +  VERIFY( f1() == 1 );
> > +  VERIFY( std::as_const(f1)() == 1 );
> > +}
> > +
> > +void
> > +test03()
> > +{
> > +  struct F
> > +  {
> > +    struct Arg {};
> > +    int operator()(Arg* arg) const { return 0; }
> > +    int operator()(const Arg* arg) const { return 1; }
> > +  };
> > +  F::Arg arg;
> > +
> > +  function_ref<int()> f0{std::nontype<F{}>, &arg};
> > +  VERIFY( f0() == 0 );
> > +  VERIFY( std::move(f0)() == 0 );
> > +
> > +  function_ref<int() const> f1{std::nontype<F{}>, &arg};
> > +  VERIFY( f1() == 1 );
> > +  VERIFY( std::as_const(f1)() == 1 );
> > +}
> > +
> > +void
> > +test04()
> > +{
> > +  constexpr int (*fp)() = [] { return 0; };
> > +  function_ref<int()> f0{fp};
> > +  VERIFY( f0() == 0 );
> > +  VERIFY( std::move(f0)() == 0 );
> > +
> > +  function_ref<int()> f1{nontype<fp>};
> > +  VERIFY( f1() == 0 );
> > +  VERIFY( std::move(f1)() == 0 );
> > +
> > +  const function_ref<int() const> f2{fp};
> > +  VERIFY( f2() == 0 );
> > +  VERIFY( std::move(f2)() == 0 );
> > +
> > +  const function_ref<int() const> f3{nontype<fp>};
> > +  VERIFY( f2() == 0 );
> > +  VERIFY( std::move(f2)() == 0 );
> > +}
> > +
> > +struct Incomplete;
> > +
> > +void
> > +test_params()
> > +{
> > +  auto f = [](auto&&) {};
> > +  // There is discussion if this is supported.
> > +  // std::function_ref<void(Incomplete)> f1(f);
> > +  std::function_ref<void(Incomplete&)> f2(f);
> > +  // See PR120259, this should be supported.
> > +  // std::function_ref<void(Incomplete&&)> f3(f);
> > +}
> > +
> > +int main()
> > +{
> > +  test01();
> > +  test02();
> > +  test03();
> > +  test04();
> > +  test_params();
> > +}
> > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> > new file mode 100644
> > index 00000000000..5860a94ac65
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
> > @@ -0,0 +1,219 @@
> > +// { dg-do compile { target c++26 } }
> > +// { dg-require-effective-target hosted }
> > +// { dg-add-options no_pch }
> > +
> > +#include <functional>
> > +
> > +#ifndef __cpp_lib_function_ref
> > +# error "Feature-test macro for function_ref missing in <functional>"
> > +#elif __cpp_lib_function_ref != 202306L
> > +# error "Feature-test macro for function_ref has wrong value in
> <functional>"
> > +#endif
> > +
> > +using std::nontype;
> > +using std::nontype_t;
> > +using std::function_ref;
> > +
> > +using std::is_default_constructible_v;
> > +using std::is_nothrow_copy_constructible_v;
> > +using std::is_nothrow_move_constructible_v;
> > +using std::is_nothrow_constructible_v;
> > +using std::is_constructible_v;
> > +using std::is_trivially_copyable_v;
> > +
> > +static_assert( ! is_default_constructible_v<function_ref<void()>> );
> > +static_assert( is_nothrow_move_constructible_v<function_ref<void()>> );
> > +static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> );
> > +static_assert( is_trivially_copyable_v<function_ref<void()>> );
> > +
> > +static_assert( ! is_constructible_v<function_ref<void()>,
> std::nullptr_t> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, void()>
> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> void(&)()> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> void(*)()> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, int()>
> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> int(&)()> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> int(*)()> );
> > +static_assert( ! is_constructible_v<function_ref<void()>, void(int)> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
> void(int)> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> > +                                       void() noexcept> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>,
> > +                                       void() noexcept> );
> > +static_assert( ! is_constructible_v<function_ref<void() noexcept>,
> > +                                 void() > );
> > +
> > +struct S
> > +{
> > +  int x;
> > +  int f();
> > +};
> > +int funS(S);
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       decltype(funS)> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       decltype(&funS)> );
> > +static_assert( ! is_constructible_v<function_ref<int(S)>,
> > +                                 decltype(&S::x)> );
> > +static_assert( ! is_constructible_v<function_ref<int(S)>,
> > +                                 decltype(&S::f)> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       nontype_t<funS>> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       nontype_t<&funS>> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       nontype_t<&S::x>> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
> > +                                       nontype_t<&S::f>> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<funS>, S&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<&funS>, S&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<&S::x>, S&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<&S::f>, S&> );
> > +
> > +static_assert( ! is_constructible_v<function_ref<int()>,
> > +                                 nontype_t<funS>, S*> );
> > +static_assert( ! is_constructible_v<function_ref<int()>,
> > +                                 nontype_t<&funS>, S*> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<&S::x>, S*> );
> > +static_assert( is_nothrow_constructible_v<function_ref<int()>,
> > +                                       nontype_t<&S::f>, S*> );
> > +
> > +struct M
> > +{
> > +  void operator()();
> > +};
> > +
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, M> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> );
> > +static_assert( ! is_constructible_v<function_ref<void()>, const M&> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>, M> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>, const
> M&> );
> > +static_assert( ! is_constructible_v<function_ref<void()>,
> > +                                 nontype_t<M{}>> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>,
> > +                                 nontype_t<M{}>> );
> > +struct Q
> > +{
> > +  void operator()(int) const;
> > +  void operator()(int*) const;
> > +};
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&>
> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
> const Q&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)
> const>, Q> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)
> const>, Q&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)
> const>, const Q&> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
> > +                                       nontype_t<Q{}>> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>,
> > +                                       nontype_t<Q{}>> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> > +                                       nontype_t<Q{}>, int&> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void() const>,
> > +                                       nontype_t<Q{}>, int&> );
> > +static_assert( ! is_constructible_v<function_ref<void()>,
> > +                                 nontype_t<Q{}>, int> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>,
> > +                                 nontype_t<Q{}>, int> );
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>,
> > +                                       nontype_t<Q{}>, int*> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>,
> > +                                 nontype_t<Q{}>, int*> );
> > +
> > +struct L
> > +{
> > +  void operator()() &;
> > +};
> > +
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, L> );
> > +static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> );
> > +static_assert( ! is_constructible_v<function_ref<void()>, const L&> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>, L> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>, const
> L&> );
> > +static_assert( ! is_constructible_v<function_ref<void()>,
> > +                                 nontype_t<L{}>> );
> > +static_assert( ! is_constructible_v<function_ref<void() const>,
> > +                                 nontype_t<L{}>> );
> > +
> > +struct R
> > +{
> > +  void operator()(float) const&&;
> > +};
> > +
> > +static_assert( ! is_constructible_v<function_ref<void(float)>, R> );
> > +static_assert( ! is_constructible_v<function_ref<void(float)>, R&> );
> > +static_assert( ! is_constructible_v<function_ref<void(float) const>, R>
> );
> > +static_assert( ! is_constructible_v<function_ref<void(float) const>,
> R&> );
> > +static_assert( ! is_constructible_v<function_ref<void(float) const>,
> const R&> );
> > +
> > +static_assert( ! is_constructible_v<function_ref<void(float)>,
> > +                                              nontype_t<R{}>> );
> > +static_assert( ! is_constructible_v<function_ref<void(float) const>,
> > +                                              nontype_t<R{}>> );
> > +
> > +constexpr bool
> > +test_constexpr()
> > +{
> > +  function_ref<void(S)> fp1(nontype<funS>);
> > +  function_ref<void(S)> fp3(nontype<&funS>);
> > +  function_ref<void(S)> fp4(nontype<&S::x>);
> > +  function_ref<void(S)> fp5(nontype<&S::f>);
> > +
> > +  S s;
> > +  function_ref<void()> fp6(nontype<&funS>, s);
> > +  function_ref<void()> fp7(nontype<&S::x>, s);
> > +  function_ref<void()> fp8(nontype<&S::x>, &s);
> > +  function_ref<void()> fp9(nontype<&S::f>, s);
> > +  function_ref<void()> fp10(nontype<&S::f>, &s);
> > +
> > +  M m;
> > +  function_ref<void()> fm1(m);
> > +  function_ref<void()> fm2(std::move(m));
> > +
> > +  Q q;
> > +  constexpr Q cq;
> > +  function_ref<void(int)> fq1(q);
> > +  function_ref<void(int) const> fq2(q);
> > +  function_ref<void(int) const> fq3(std::move(q));
> > +
> > +  function_ref<void(int)> fcq1(cq);
> > +  function_ref<void(int) const> f(cq);
> > +  function_ref<void(int)> fcq3(nontype<cq>);
> > +  function_ref<void(int) const> fcq4(nontype<cq>);
> > +
> > +  int i = 0;
> > +  function_ref<void()> fcq5(nontype<cq>, i);
> > +  function_ref<void() const> fcq6(nontype<cq>, i);
> > +  function_ref<void()> fcq7(nontype<cq>, &i);
> > +
> > +  L l;
> > +  function_ref<void()> fl1(l);
> > +  function_ref<void()> fl2(std::move(l));
> > +
> > +  return true;
> > +}
> > +static_assert( test_constexpr() );
> > +
> > +void func();
> > +
> > +void
> > +test_instantiation()
> > +{
> > +  function_ref<void(S)> fp1(funS);
> > +  function_ref<void(S)> fp2(&funS);
> > +
> > +  test_constexpr();
> > +}
> > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
> > new file mode 100644
> > index 00000000000..c665532962a
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
> > @@ -0,0 +1,30 @@
> > +// { dg-do compile { target c++26 } }
> > +
> > +#include <functional>
> > +
> > +using std::nontype;
> > +using std::function_ref;
> > +
> > +struct S
> > +{
> > +  int x;
> > +  void foo();
> > +};
> > +S s;
> > +
> > +constexpr int(*fp)(S) = nullptr;
> > +constexpr int S::*mdp = nullptr;
> > +constexpr int (S::*mfp)() = nullptr;
> > +
> > +function_ref<int(S)> fd1(nontype<fp>);  // { dg-error "from here" }
> > +function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" }
> > +function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" }
> > +
> > +function_ref<int()> br4(nontype<fp>, s);  // { dg-error "from here" }
> > +function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" }
> > +function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" }
> > +
> > +function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" }
> > +function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" }
> > +
> > +// { dg-prune-output "static assertion failed" }
> > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> > new file mode 100644
> > index 00000000000..96379fde43a
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> > @@ -0,0 +1,152 @@
> > +// { dg-do run { target c++26 } }
> > +// { dg-require-effective-target hosted }
> > +
> > +#include <functional>
> > +#include <testsuite_hooks.h>
> > +
> > +using std::function_ref;
> > +
> > +static_assert( std::is_constructible_v<std::function_ref<void() const>,
> > +                                    std::function_ref<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;
> > +
> > +// The C++26 [func.wrap.general] p2 does not currently cover
> funciton_ref,
> > +// so we make extra copies of arguments.
> > +
> > +void
> > +test01()
> > +{
> > +  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
> > +  std::function_ref<int(CountedArg) const noexcept> r1(f);
> > +  std::move_only_function<int(CountedArg) const noexcept> m1(f);
> > +  std::copyable_function<int(CountedArg) const noexcept> c1(f);
> > +
> > +  // Complatible signatures
> > +  std::function_ref<int(CountedArg) const noexcept> r2m(m1);
> > +  VERIFY( r2m(c) == 2 );
> > +  std::function_ref<int(CountedArg) const noexcept> r2c(c1);
> > +  VERIFY( r2c(c) == 2 );
> > +
> > +  std::function_ref<int(CountedArg) const> r3r(r1);
> > +  VERIFY( r3r(c) == 2 );
> > +  std::function_ref<int(CountedArg) const> r3m(m1);
> > +  VERIFY( r3m(c) == 2 );
> > +  std::function_ref<int(CountedArg) const> r3c(m1);
> > +  VERIFY( r3c(c) == 2 );
> > +
> > +  std::function_ref<int(CountedArg)> r4r(r1);
> > +  VERIFY( r4r(c) == 2 );
> > +  std::function_ref<int(CountedArg)> r4m(m1);
> > +  VERIFY( r4m(c) == 2 );
> > +  std::function_ref<int(CountedArg)> r4c(m1);
> > +  VERIFY( r4c(c) == 2 );
> > +
> > +  // Incompatible signatures
> > +  std::function_ref<long(CountedArg) const noexcept> r5r(r1);
> > +  VERIFY( r5r(c) == 2 );
> > +  std::function_ref<long(CountedArg) const noexcept> r5m(m1);
> > +  VERIFY( r5r(c) == 2 );
> > +  std::function_ref<long(CountedArg) const noexcept> r5c(c1);
> > +  VERIFY( r5r(c) == 2 );
> > +}
> > +
> > +void
> > +test02()
> > +{
> > +  // Constructing move_only_function and copyable_function from
> function_ref,
> > +  // have not chance to restore manager, so we store function_ref
> inside.
> > +  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
> > +  std::function_ref<int(CountedArg) const noexcept> r1(f);
> > +
> > +  std::move_only_function<int(CountedArg) const noexcept> m1(r1);
> > +  VERIFY( m1(c) == 2 );
> > +
> > +  std::copyable_function<int(CountedArg) const noexcept> c1(r1);
> > +  VERIFY( c1(c) == 2 );
> > +}
> > +
> > +void
> > +test03()
> > +{
> > +  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::function_ref<int(CountedArg) const> r1(f);
> > +  VERIFY( r1(c) == 1001 );
> > +
> > +  // Call const overload as std::function_ref<int(CountedArg) const>
> > +  // inside std::function_ref<int(CountedArg)> would do.
> > +  std::function_ref<int(CountedArg)> r2(r1);
> > +  VERIFY( r2(c) == 1002 );
> > +  std::move_only_function<int(CountedArg)> m2(r1);
> > +  VERIFY( m2(c) == 1002 );
> > +
> > +  // Call non-const overload as const-qualifed operator() for
> > +  // std::function_ref<int(CountedArg)> do.
> > +  std::function_ref<int(CountedArg)> r3(f);
> > +  VERIFY( r3(c) == 1 );
> > +  std::function_ref<int(CountedArg) const> r4(r3);
> > +  VERIFY( r4(c) == 2 );
> > +  std::move_only_function<int(CountedArg) const> m4(r3);
> > +  VERIFY( m4(c) == 2 );
> > +}
> > +
> > +void
> > +test04()
> > +{
> > +  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
> > +  std::function_ref<int(CountedArg)> w1(f);
> > +  // function_ref stores function_ref due incompatibile signatures
> > +  std::function_ref<int(CountedArg const&)> w2(std::move(w1));
> > +  // copy is made when passing to int(CountedArg)
> > +  VERIFY( w2(c) == 1 );
> > +  // wrapped 3 times
> > +  std::function_ref<int(CountedArg)> w3(w2);
> > +  VERIFY( w3(c) == 2 );
> > +  // wrapped 4 times
> > +  std::function_ref<int(CountedArg const&)> w4(w3);
> > +  VERIFY( w4(c) == 2 );
> > +  // wrapped 5 times
> > +  std::function_ref<int(CountedArg)> w5(w4);
> > +  VERIFY( w5(c) == 3 );
> > +}
> > +
> > +void
> > +test05()
> > +{
> > +  // No special interoperability with std::function
> > +  auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
> > +  std::function<int(CountedArg)> f1(f);
> > +  std::function_ref<int(CountedArg) const> c1(std::move(f1));
> > +  VERIFY( c1(c) == 2 );
> > +
> > +  std::function_ref<int(CountedArg) const> c2(f);
> > +  std::function<int(CountedArg)> f2(c2);
> > +  VERIFY( f2(c) == 2 );
> > +}
> > +
> > +int main()
> > +{
> > +  test01();
> > +  test02();
> > +  test03();
> > +  test04();
> > +  test05();
> > +}
> > --
> > 2.49.0
> >
> >

Reply via email to