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 > > > >