On Thu, 15 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 casts, a __polyfunc::__cast_to helper is > added, that accepts reference to or target type direclty. > > 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 wrapper, 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 are currently only available in > hosted, > 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. > (__polyfunc::__skip_first_arg, __polyfunc::__deduce_funcref) > (std::function_ref) [__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. > * include/std/functional: Define __cpp_lib_function_ref. > * src/c++23/std.cc.in (std::nontype_t, std::nontype) > (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. > * testsuite/20_util/function_ref/deduction.cc: New test. > --- > This patch handles using nontype with function pointer/reference. > In function_ref we now use _M_init(ptr) function, that handles > both function and object pointers. The __polyfunc::__cast_to is > also expaned to handle function_types. > > 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 | 198 ++++++++++++++++ > libstdc++-v3/include/bits/funcwrap.h | 185 +++++++++++---- > 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 | 3 +- > libstdc++-v3/src/c++23/std.cc.in | 7 + > .../testsuite/20_util/function_ref/assign.cc | 108 +++++++++ > .../testsuite/20_util/function_ref/call.cc | 186 +++++++++++++++ > .../testsuite/20_util/function_ref/cons.cc | 218 ++++++++++++++++++ > .../20_util/function_ref/cons_neg.cc | 30 +++ > .../testsuite/20_util/function_ref/conv.cc | 152 ++++++++++++ > .../20_util/function_ref/deduction.cc | 103 +++++++++ > 16 files changed, 1181 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 > create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/deduction.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..7be5060acf7 > --- /dev/null > +++ b/libstdc++-v3/include/bits/funcref_impl.h > @@ -0,0 +1,198 @@ > +// 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 > + > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > + > + /// @cond undocumented > + namespace __polyfunc > + { > + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> > + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV > + noexcept(_Noex)> > + { using type = _Ret(_Args...) noexcept(_Noex); }; > + > + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> > + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV& > + noexcept(_Noex)> > + { using type = _Ret(_Args...) noexcept(_Noex); }; > + } // namespace __polyfunc > + /// @endcond > + > + /** > + * @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-qualification and no-throw guarantees. The > qualifications and > + * 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*>();
All these explicit 'this->' seem unnecessary/inconsistent? > + this->_M_init(__fn); > + } > + > + /// Target and bound object is object referenced by parameter. > + 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_CV&> > + constexpr > + function_ref(_Fn&& __f) noexcept > + { > + this->_M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>(); > + this->_M_init(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 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_CV&> > + 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_CV&; > + 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_init(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_init(__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)...); > } > + > + private: > + template<typename _Tp> > + constexpr void > + _M_init(_Tp* __ptr) noexcept > + { > + if constexpr (is_function_v<_Tp>) > + _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr); > + else > + _M_ptrs._M_obj = __ptr; > + } > + > + typename _Invoker::__ptrs_func_t _M_invoke; > + __polyfunc::_Ptrs _M_ptrs; > + }; > + > +#undef _GLIBCXX_MOF_CV > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace std > diff --git a/libstdc++-v3/include/bits/funcwrap.h > b/libstdc++-v3/include/bits/funcwrap.h > index 4e053534cf5..639b910afb2 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,7 @@ > > #include <bits/version.h> > > -#if defined(__glibcxx_move_only_function) || > defined(__glibcxx_copyable_function) > +#if __glibcxx_move_only_function || __glibcxx_copyable_function || > __glibcxx_function_ref > > #include <bits/invoke.h> > #include <bits/utility.h> > @@ -53,10 +54,24 @@ _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 > + { > + using _Td = remove_reference_t<_Tp>; > + if constexpr (is_function_v<_Td>) > + return reinterpret_cast<_Td*>(__ptrs._M_func); > + else 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 +112,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 +132,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); I guess we can use auto* here and in the other __cast_to call sites for more clarity. > + 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 +171,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 +224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return false; > } > > +#if __glibcxx_move_only_function || __glibcxx_copyable_function > struct _Manager > { > enum class _Op > @@ -241,7 +282,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 +304,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 +339,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 +427,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 +513,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } // namespace __detail::__variant > #endif // __glibcxx_copyable_function > > +#ifdef __glibcxx_function_ref // C++ >= 26 > + /// @cond undocumented > + namespace __polyfunc > + { > + template<typename _Sig> > + struct __skip_first_arg; > + > + template<bool _Noex, typename _Ret, typename _Arg, typename... _Args> > + struct __skip_first_arg<_Ret(*)(_Arg, _Args...) noexcept(_Noex)> > + { using type = _Ret(_Args...) noexcept(_Noex); }; Can you add a comment that the other partial specializations are defined in funcref_impl.h? > + > + template<typename _Fn, typename _Tr> > + consteval auto __deduce_funcref() Missing newline. LGTM besides that. > + { > + if constexpr (is_member_object_pointer_v<_Fn>) > + // TODO Consider reporting issue to make this noexcept > + return static_cast<invoke_result_t<_Fn, _Tr>(*)()>(nullptr); > + else > + return static_cast<__skip_first_arg<_Fn>::type*>(nullptr); > + } > + } // namespace __polyfunc > + /// @endcond > + > + template<typename... _Signature> > + class function_ref; // not defined > + > + template<typename _Fn> > + requires is_function_v<_Fn> > + function_ref(_Fn*) -> function_ref<_Fn>; > + > + template<auto __f, class _Fn = remove_pointer_t<decltype(__f)>> > + requires is_function_v<_Fn> > + function_ref(nontype_t<__f>) -> function_ref<_Fn>; > + > + template<auto __f, typename _Tp, class _Fn = decltype(__f)> > + requires is_member_pointer_v<_Fn> || is_function_v<remove_pointer_t<_Fn>> > + function_ref(nontype_t<__f>, _Tp&&) > + -> function_ref< > + remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn, > _Tp&>())>>; > + > +#endif // __glibcxx_function_ref > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > > @@ -503,5 +590,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..307bcb95bcc 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,7 @@ > # 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 __glibcxx_move_only_function || __glibcxx_copyable_function || > __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..ba468530338 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; > #endif > using std::multiplies; > using std::negate; > @@ -3195,6 +3198,10 @@ export namespace std > using std::make_integer_sequence; > using std::move; > using std::move_if_noexcept; > +#if __cpp_lib_function_ref > + using std::nontype_t; > + using std::nontype; > +#endif > using std::pair; > using std::swap; > using std::operator==; > 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..9b02dc49c2a > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc > @@ -0,0 +1,108 @@ > +// { dg-do compile { target c++26 } } > + > +#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..a91c6b4abb9 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc > @@ -0,0 +1,186 @@ > +// { dg-do run { target c++26 } } > + > +#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 ); > +} > + > +using ftype = int(int); > +int twice(int x) { return x * 2; } > +int cube(int x) { return x * x * x; } > +int callback_ptr(ftype* f, int x) { return f(x); } > +int callback_ref(ftype& f, int x) { return f(x); } > + > +void > +test05() > +{ > + > + function_ref<int(int)> r1(nontype<&callback_ptr>, &twice); > + VERIFY( r1(2) == 4 ); > + function_ref<int(int)> r2(nontype<&callback_ptr>, cube); > + VERIFY( r2(2) == 8 ); > + > + function_ref<int(int)> r3(nontype<&callback_ref>, twice); > + VERIFY( r3(3) == 6 ); > + function_ref<int(int)> r4(nontype<&callback_ref>, cube); > + VERIFY( r4(3) == 27 ); > + > + // Checks if distinction between reference and pointer > + // is preserved. > + struct F > + { > + static > + int operator()(ftype* f, int x) > + { return f(x) + 1000; } > + > + static > + int operator()(ftype& f, int x) > + { return f(x) + 2000; } > + }; > + function_ref<int(int)> r5(nontype<F{}>, &twice); > + VERIFY( r5(2) == 1004 ); > + function_ref<int(int)> r6(nontype<F{}>, twice); > + VERIFY( r6(2) == 2008 ); > + function_ref<int(int)> r7(nontype<F{}>, &cube); > + VERIFY( r7(3) == 1006 ); > + function_ref<int(int)> r8(nontype<F{}>, cube); > + VERIFY( r8(3) == 2027 ); > +} > + > +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..a91f5ba3dab > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc > @@ -0,0 +1,218 @@ > +// { dg-do compile { target c++26 } } > +// { 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..050090df370 > --- /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..49ff0a78829 > --- /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(c1); > + 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(c1); > + 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(); > +} > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc > b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc > new file mode 100644 > index 00000000000..2940b876954 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc > @@ -0,0 +1,103 @@ > +// { dg-do compile { target c++26 } } > + > +#include <functional> > +#include <type_traits> > + > +using std::is_same_v; > +using std::nontype; > +using std::nontype_t; > +using std::function_ref; > + > +int i = 0; > + > +void f0(); > +void f0n() noexcept; > + > +static_assert( is_same_v<decltype(function_ref(f0)), > + function_ref<void()>> ); > +static_assert( is_same_v<decltype(function_ref(f0n)), > + function_ref<void() noexcept>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f0>)), > + function_ref<void()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f0n>)), > + function_ref<void() noexcept>> ); > + > +void f1(int); > +void f1n(int) noexcept; > + > +static_assert( is_same_v<decltype(function_ref(f1)), > + function_ref<void(int)>> ); > +static_assert( is_same_v<decltype(function_ref(f1n)), > + function_ref<void(int) noexcept>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f1>)), > + function_ref<void(int)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f1n>)), > + function_ref<void(int) noexcept>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)), > + function_ref<void()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)), > + function_ref<void() noexcept>> ); > + > +void f2(int*, int); > +void f2n(int*, int) noexcept; > + > +static_assert( is_same_v<decltype(function_ref(f2)), > + function_ref<void(int*, int)>> ); > +static_assert( is_same_v<decltype(function_ref(f2n)), > + function_ref<void(int*, int) noexcept>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f2>)), > + function_ref<void(int*, int)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f2n>)), > + function_ref<void(int*, int) noexcept>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)), > + function_ref<void(int)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)), > + function_ref<void(int) noexcept>> ); > + > +struct S > +{ > + int mem; > + int f(); > + int fn() noexcept; > + > + int fc(int) const; > + int fcn(int) const noexcept; > + > + int fl(int) &; > + int fln(int) & noexcept; > + > + int fcl(float) const&; > + int fcln(float) const& noexcept; > +}; > +S s{}; > +const S cs{}; > + > +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)), > + function_ref<int&()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)), > + function_ref<const int&()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)), > + function_ref<int&()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)), > + function_ref<const int&()>> ); > + > +static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)), > + function_ref<int()>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)), > + function_ref<int() noexcept>> ); > + > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)), > + function_ref<int(int)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)), > + function_ref<int(int) noexcept>> ); > + > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)), > + function_ref<int(int)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)), > + function_ref<int(int) noexcept>> ); > + > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)), > + function_ref<int(float)>> ); > +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)), > + function_ref<int(float) noexcept>> ); > + > -- > 2.49.0 > >