https://gcc.gnu.org/g:c56ec55c797daee08681354b229f4475558c1342
commit r17-353-gc56ec55c797daee08681354b229f4475558c1342 Author: Tomasz Kamiński <[email protected]> Date: Thu May 15 17:04:22 2025 +0200 libstdc++: Avoid double indirection in function_ref when possible This implements the P3961R1: Less double indirection in function_ref. This patch uses provision provided by the paper for implementations, and expands the set of the compatible signatures to include ParamType&& and ParamType, and by value return types that differs only in cv-qualifiers. This follows the move_only_function approach from the r16-617-g708d40ff109c6e. Futhermore the optimization is also applied when function_ref<Ret(Args...) const> is constructed from function_ref<Ret(Args...)>, even if the underyling target is be mutated by the call. The implementations moves the _M_ptrs members to newly defined base class __polyfunc::_Ref_base. This allows us to reuse existing __base_of and __invoker_of accessor in the implementation (after befriending them). The accessors functions are also now marked as constexpr. Furthermore, _Ref_base default constructor initializes the _M_ptrs._M_obj to nullptr, and _M_init function is transfered to it, making it instantiations independent from callback signature. To check signature compatiblity required by standard for assignment, new __is_funcref_assignable function is used, and _ArgsSignature and _TargetQuals member typedef are defined in function_ref. To avoid confusion between adjusted and specified signature, _Signature typedef is removed from all wrappers, and __is_invoker_convertible is updated to use _Invoker::_Signature instead. Per SG-10 guidance __cpp_lib_function_ref feature test macro is updated to 202604, differntiating from P3948R1 paper accepted at the same time. PR libstdc++/119126 libstdc++-v3/ChangeLog: * include/bits/funcwrap.h : (__polyfunc::__invoker_of): Updated to use _Invoker::_Signature and mark as constexpr. (__polyfunc::_base_of): Mark as constexpr. (__std:::__is_function_ref_v, __polyfunc::_Ref_base) (__polyfunc::__is_funcref_assignable): Define. * include/bits/funcref_impl.h (std::function_ref): Add base class of type__polyfunc::_Ref_base. Befriend __invoker_of, __base_of, __is_invoker_convertible, __is_invoker_convertible. (function_ref::_Base): Define. (function_ref::_M_init, function_ref::_M_ptrs): Move to base class. (function_ref::function_ref(_Fn&&), function_ref::operator=): Handle specializations of function_ref with compatible signatures. (function_ref::function_ref): Init base class before _M_invoke consistently, and remove setting of _M_nullptr. * include/bits/cpyfunc_impl.h (copyable_function): Udpdated friend declarations. (copyable_function::_Signature): Remove. * include/bits/mofunc_impl.h (move_only_function): Udpdated friend declarations. (move_only_function::_Signature): Remove. * include/bits/version.def (function_ref): Bump to 202604. * include/bits/version.h: Regnerate. * testsuite/20_util/function_ref/cons.cc: Updated checked FTM value. * testsuite/20_util/function_ref/conv.cc: Updated test to illustrate that double indirection is avoided. * testsuite/20_util/function_ref/dangling.cc: Test for initializing from function_ref with compatible signature. * testsuite/20_util/function_ref/dangling_neg.cc: Test for initializing from function_ref with incompatible signature. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/bits/cpyfunc_impl.h | 7 +- libstdc++-v3/include/bits/funcref_impl.h | 57 +++++++----- libstdc++-v3/include/bits/funcwrap.h | 51 +++++++++- libstdc++-v3/include/bits/mofunc_impl.h | 7 +- libstdc++-v3/include/bits/version.def | 3 +- libstdc++-v3/include/bits/version.h | 4 +- .../testsuite/20_util/function_ref/cons.cc | 2 +- .../testsuite/20_util/function_ref/conv.cc | 103 ++++++++++++++++----- .../testsuite/20_util/function_ref/dangling.cc | 17 +++- .../testsuite/20_util/function_ref/dangling_neg.cc | 8 ++ 10 files changed, 194 insertions(+), 65 deletions(-) diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h b/libstdc++-v3/include/bits/cpyfunc_impl.h index f1918ddf87a3..580295a5d635 100644 --- a/libstdc++-v3/include/bits/cpyfunc_impl.h +++ b/libstdc++-v3/include/bits/cpyfunc_impl.h @@ -70,7 +70,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using _Base = __polyfunc::_Cpy_base; using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; - using _Signature = _Invoker::_Signature; template<typename _Tp> using __callable @@ -252,14 +251,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _Invoker::__storage_func_t _M_invoke = nullptr; template<typename _Func> - friend auto& + friend constexpr auto& __polyfunc::__invoker_of(_Func&) noexcept; template<typename _Func> - friend auto& + friend constexpr auto& __polyfunc::__base_of(_Func&) noexcept; - template<typename _Dst, typename _Src> + template<typename _Src, typename _Dst> friend consteval bool __polyfunc::__is_invoker_convertible() noexcept; }; diff --git a/libstdc++-v3/include/bits/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h index 9fcab570803f..9fc8432b42be 100644 --- a/libstdc++-v3/include/bits/funcref_impl.h +++ b/libstdc++-v3/include/bits/funcref_impl.h @@ -67,13 +67,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Res, typename... _ArgTypes, bool _Noex> class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV noexcept(_Noex)> + : __polyfunc::_Ref_base { static_assert( (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...), "each parameter type must be a complete class"); + using _Base = __polyfunc::_Ref_base; using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; - using _Signature = _Invoker::_Signature; + using _ArgsSignature = _Res(_ArgTypes...) noexcept(_Noex); + using _TargetQuals = int _GLIBCXX_MOF_CV&; // [func.wrap.ref.ctor]/1 is-invokable-using template<typename... _Tps> @@ -89,8 +92,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION function_ref(_Fn* __fn) noexcept { __glibcxx_assert(__fn != nullptr); - _M_invoke = _Invoker::template _S_ptrs<_Fn*>(); _M_init(__fn); + _M_invoke = _Invoker::template _S_ptrs<_Fn*>(); } /// Target and bound object is object referenced by parameter. @@ -108,21 +111,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { using _Fd = remove_cv_t<_Vt>; if constexpr (__is_std_op_wrapper<_Fd>) - { - _M_invoke = _Invoker::template _S_nttp<_Fd{}>; - _M_ptrs._M_obj = nullptr; - } + _M_invoke = _Invoker::template _S_nttp<_Fd{}>; else if constexpr (requires (_ArgTypes&&... __args) { _Fd::operator()(std::forward<_ArgTypes>(__args)...); }) + _M_invoke = _Invoker::template _S_static<_Fd>; + else if constexpr (__is_function_ref_v<_Fd> + && __polyfunc::__is_invoker_convertible<_Fd, function_ref>()) { - _M_invoke = _Invoker::template _S_static<_Fd>; - _M_ptrs._M_obj = nullptr; + _Base::operator=(__polyfunc::__base_of(__f)); + _M_invoke = __polyfunc::__invoker_of(__f); } else { - _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>(); _M_init(std::addressof(__f)); + _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>(); } } @@ -146,7 +149,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(__fn != nullptr); _M_invoke = &_Invoker::template _S_nttp<__fn>; - _M_ptrs._M_obj = nullptr; } /// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)). @@ -162,6 +164,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) static_assert(__fn != nullptr); + _M_init(std::addressof(__ref)); if constexpr (is_member_pointer_v<_Fn> && same_as<_Td, typename __inv_unwrap<_Td>::type>) // N.B. invoking member pointer on lvalue produces the same effects, @@ -169,7 +172,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; else _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Td _GLIBCXX_MOF_CV&>; - _M_init(std::addressof(__ref)); } /// Target object is equivalent to std::bind_front<_fn>(__ptr). @@ -185,13 +187,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (is_member_pointer_v<_Fn>) __glibcxx_assert(__ptr != nullptr); - _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; _M_init(__ptr); + _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; } template<typename _Tp> - requires (!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>) + requires (!is_same_v<_Tp, function_ref>) + && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper_v<_Tp>) + && (!__polyfunc::__is_funcref_assignable<function_ref, _Tp>()) function_ref& operator=(_Tp) = delete; @@ -206,18 +210,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_invoke(_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; + + template<typename _Func> + friend constexpr auto& + __polyfunc::__invoker_of(_Func&) noexcept; + + template<typename _Func> + friend constexpr auto& + __polyfunc::__base_of(_Func&) noexcept; + + template<typename _Src, typename _Dst> + friend consteval bool + __polyfunc::__is_invoker_convertible() noexcept; + + template<typename _Dst, typename _Src> + friend consteval bool + __polyfunc::__is_funcref_assignable() noexcept; }; #undef _GLIBCXX_MOF_CV diff --git a/libstdc++-v3/include/bits/funcwrap.h b/libstdc++-v3/include/bits/funcwrap.h index b835e075295b..07163f3a5a32 100644 --- a/libstdc++-v3/include/bits/funcwrap.h +++ b/libstdc++-v3/include/bits/funcwrap.h @@ -231,12 +231,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>, __param_t<_Args>...>; template<typename _Func> - auto& + constexpr auto& __invoker_of(_Func& __f) noexcept { return __f._M_invoke; } template<typename _Func> - auto& + constexpr auto& __base_of(_Func& __f) noexcept { return static_cast<__like_t<_Func&, typename _Func::_Base>>(__f); } @@ -244,9 +244,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION consteval bool __is_invoker_convertible() noexcept { - if constexpr (requires { typename _Src::_Signature; }) - return is_convertible_v<typename _Src::_Signature, - typename _Dst::_Signature>; + if constexpr (requires { typename _Src::_Invoker::_Signature; }) + return is_convertible_v<typename _Src::_Invoker::_Signature, + typename _Dst::_Invoker::_Signature>; else return false; } @@ -539,6 +539,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @cond undocumented namespace __polyfunc { + struct _Ref_base + { + constexpr + _Ref_base() noexcept + { _M_ptrs._M_obj = nullptr; } + + 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; + } + + _Ptrs _M_ptrs; + }; + template<typename _Sig> struct __skip_first_arg; @@ -585,6 +604,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION function_ref(constant_wrapper<__cwfn, _Fn>, _Tp&&) -> function_ref<remove_pointer_t<_SignaturePtr>>; + /// @cond undocumented + template<typename _Tp> + constexpr bool __is_function_ref_v = false; + template<typename _Tp> + constexpr bool __is_function_ref_v<function_ref<_Tp>> = true; + + namespace __polyfunc + { + template<typename _Dst, typename _Src> + consteval bool + __is_funcref_assignable() noexcept + { + if constexpr (__is_function_ref_v<_Src>) + if constexpr (is_convertible_v<typename _Src::_ArgsSignature*, + typename _Dst::_ArgsSignature*>) + return is_convertible_v<typename _Dst::_TargetQuals, + typename _Src::_TargetQuals>; + return false; + } + } // namespace __polyfunc + /// @endcond + #endif // __glibcxx_function_ref _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/include/bits/mofunc_impl.h b/libstdc++-v3/include/bits/mofunc_impl.h index 468e6855fc6b..91a78bfca018 100644 --- a/libstdc++-v3/include/bits/mofunc_impl.h +++ b/libstdc++-v3/include/bits/mofunc_impl.h @@ -70,7 +70,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using _Base = __polyfunc::_Mo_base; using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; - using _Signature = _Invoker::_Signature; template<typename _Tp> using __callable @@ -236,14 +235,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _Invoker::__storage_func_t _M_invoke = nullptr; template<typename _Func> - friend auto& + friend constexpr auto& __polyfunc::__invoker_of(_Func&) noexcept; template<typename _Func> - friend auto& + friend constexpr auto& __polyfunc::__base_of(_Func&) noexcept; - template<typename _Dst, typename _Src> + template<typename _Src, typename _Dst> friend consteval bool __polyfunc::__is_invoker_convertible() noexcept; }; diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 4db17a6d98c7..1f0d3a2670e5 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1985,9 +1985,10 @@ ftms = { // 202306 P0792R14 function_ref: a non-owning reference to a Callable // 202511 P3774R1 Rename std::nontype, and make it broadly useful // 202603 P3948R1 constant_wrapper is the only tool needed for passing... + // 202604 P3961R1 Less double indirection in function_ref (RU-220) name = function_ref; values = { - v = 202603; + v = 202604; cxxmin = 26; }; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 62bc83f7ea12..66ac0ebef680 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2208,9 +2208,9 @@ #if !defined(__cpp_lib_function_ref) # if (__cplusplus > 202302L) -# define __glibcxx_function_ref 202603L +# define __glibcxx_function_ref 202604L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_function_ref) -# define __cpp_lib_function_ref 202603L +# define __cpp_lib_function_ref 202604L # endif # endif #endif /* !defined(__cpp_lib_function_ref) */ diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc index e6c13e3e538c..33f30099c54e 100644 --- a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc @@ -5,7 +5,7 @@ #ifndef __cpp_lib_function_ref # error "Feature-test macro for function_ref missing in <functional>" -#elif __cpp_lib_function_ref != 202603L +#elif __cpp_lib_function_ref != 202604L # error "Feature-test macro for function_ref has wrong value in <functional>" #endif diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc index 7606d265f986..8c6d6b66a8b8 100644 --- a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc +++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc @@ -36,8 +36,33 @@ static_assert( std::is_same_v<std::function_ref<void(int const[5])>, static_assert( std::is_same_v<std::function_ref<void(FuncType)>, std::function_ref<void(FuncType*)>>); -// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref, -// so we make extra copies of arguments. +// Compatible signatures per standard, function_ref is assignable +static_assert( std::is_assignable_v<std::function_ref<int(long) noexcept>&, + std::function_ref<int(long) const noexcept>> ); +static_assert( std::is_assignable_v<std::function_ref<int(long) const>&, + std::function_ref<int(long) const noexcept>> ); +static_assert( std::is_assignable_v<std::function_ref<int(long)>&, + std::function_ref<int(long) const noexcept>> ); + +// Incompatible signatures per standard, not assignable +static_assert( !std::is_assignable_v<std::function_ref<int(long) const>&, + std::function_ref<int(long)>> ); +static_assert( !std::is_assignable_v<std::function_ref<int(int)>&, + std::function_ref<int(long)>> ); +static_assert( !std::is_assignable_v<std::function_ref<long(long)>&, + std::function_ref<int(long)>> ); +static_assert( !std::is_assignable_v<std::function_ref<long(int)>&, + std::function_ref<int(long)>> ); + +// Implementation-specific compatible signatures, function_ref is not assingable +static_assert( !std::is_assignable_v<std::function_ref<int(CountedArg)>&, + std::function_ref<int(CountedArg&&)>> ); +static_assert( !std::is_assignable_v<std::function_ref<CountedArg()>&, + std::function_ref<const CountedArg(long)>> ); +static_assert( !std::is_assignable_v<std::function_ref<const CountedArg()>&, + std::function_ref<CountedArg(long)>> ); +static_assert( !std::is_assignable_v<std::function_ref<int(long) const>&, + std::function_ref<int(long)>> ); void test01() @@ -54,26 +79,40 @@ test01() VERIFY( r2c(c) == 2 ); std::function_ref<int(CountedArg) const> r3r(r1); - VERIFY( r3r(c) == 2 ); + VERIFY( r3r(c) == 1 ); 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); + std::function_ref<int(CountedArg) noexcept> r4r(r1); + VERIFY( r4r(c) == 1 ); + std::function_ref<int(CountedArg) noexcept> r4m(m1); VERIFY( r4m(c) == 2 ); - std::function_ref<int(CountedArg)> r4c(c1); + std::function_ref<int(CountedArg) noexcept> r4c(c1); VERIFY( r4c(c) == 2 ); + std::function_ref<int(CountedArg)> r5r(r1); + VERIFY( r4r(c) == 1 ); + std::function_ref<int(CountedArg)> r5m(m1); + VERIFY( r4m(c) == 2 ); + std::function_ref<int(CountedArg)> r5c(c1); + VERIFY( r4c(c) == 2 ); + + r3r = r1; + VERIFY( r3r(c) == 1 ); + r4r = r1; + VERIFY( r4r(c) == 1 ); + r5r = r1; + VERIFY( r5r(c) == 1 ); + // 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 ); + std::function_ref<long(CountedArg) const noexcept> r6r(r1); + VERIFY( r6r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r6m(m1); + VERIFY( r6r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r6c(c1); + VERIFY( r6r(c) == 2 ); } void @@ -110,7 +149,7 @@ test03() // 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 ); + VERIFY( r2(c) == 1001 ); std::move_only_function<int(CountedArg)> m2(r1); VERIFY( m2(c) == 1002 ); @@ -119,7 +158,7 @@ test03() std::function_ref<int(CountedArg)> r3(f); VERIFY( r3(c) == 1 ); std::function_ref<int(CountedArg) const> r4(r3); - VERIFY( r4(c) == 2 ); + VERIFY( r4(c) == 1 ); std::move_only_function<int(CountedArg) const> m4(r3); VERIFY( m4(c) == 2 ); } @@ -234,7 +273,7 @@ test07() std::function_ref<int()> r3r(r1); VERIFY( r3r() == 2 ); r1 = f1; - VERIFY( r3r() == 1 ); // converting-constructor + VERIFY( r3r() == 2 ); // converting-constructor std::function_ref<int()> r3m(m1); VERIFY( r3m() == 2 ); @@ -263,6 +302,19 @@ test08() void test09() { + auto f = [](CountedArg arg) noexcept { return arg.counter; }; + // For non-tirival types Param and Param&& are compatible. + std::function_ref<int(CountedArg) const noexcept> r1(f); + VERIFY( r1({}) == 1 ); + std::function_ref<int(CountedArg&&) const noexcept> r2(r1); + VERIFY( r2({}) == 1 ); + + auto fs = [](CountedArg const& arg, std::string) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg, std::string) const noexcept> rs1(fs); + VERIFY( rs1(c, "") == 1 ); + std::function_ref<int(CountedArg, std::string&&) const noexcept> rs2(rs1); + VERIFY( rs2(c, "") == 1 ); + // Scalar types and small trivially move constructible types are passed // by value to invoker. So int&& signature is not compatible for such types. auto fi = [](CountedArg const& arg, int) noexcept { return arg.counter; }; @@ -271,11 +323,20 @@ test09() std::function_ref<int(CountedArg, int&&) const noexcept> ri2(ri1); VERIFY( ri2(c, 0) == 2 ); - auto fs = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; }; - std::function_ref<int(CountedArg, std::string_view) const noexcept> rs1(fs); - VERIFY( rs1(c, "") == 1 ); - std::function_ref<int(CountedArg, std::string_view&&) const noexcept> rs2(rs1); - VERIFY( rs2(c, "") == 2 ); + auto fv = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg, std::string_view) const noexcept> rv1(fv); + VERIFY( rv1(c, "") == 1 ); + std::function_ref<int(CountedArg, std::string_view&&) const noexcept> rv2(rv1); + VERIFY( rv2(c, "") == 2 ); + + // CV-qual on by-value return is also ignored + auto fr = [](CountedArg const& arg) noexcept { return arg; }; + std::function_ref<CountedArg(CountedArg) const noexcept> rr1(fr); + VERIFY( rr1(c).counter == 2 ); + std::function_ref<const CountedArg(CountedArg) const noexcept> rr2(rr1); + VERIFY( rr2(c).counter == 2 ); + std::function_ref<CountedArg(CountedArg)> rr3(rr2); + VERIFY( rr3(c).counter == 2 ); } int main() diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc index 4ef5d067555d..4a7b102c7889 100644 --- a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc +++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc @@ -1,12 +1,13 @@ // { dg-do compile { target c++26 } } #include <functional> +#include <string> -template<typename F> -constexpr std::function_ref<int(int, int) const> +template<typename Signature = int(int, int) const, typename F> +constexpr std::function_ref<Signature> create(F f) { - std::function_ref<int(int, int) const> fr(f); + std::function_ref<Signature> fr(f); return fr; } @@ -72,3 +73,13 @@ struct StaticWinsET { constexpr auto vStaticWinsET = create(StaticWinsET{}); +auto func = [](std::string s) noexcept -> int +{ return s.size(); }; + +constexpr std::function_ref<int(std::string)> stdCompatibleFRef + = std::function_ref<int(std::string) const noexcept>(func); +constexpr std::function_ref<int(std::string&&)> implCompatible1FRef + = std::function_ref<int(std::string)>(func); +constexpr std::function_ref<int(std::string) const> implCompatible2FRef + = std::function_ref<int(std::string)>(func); + diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc index 16033014703e..4e99f82a50cc 100644 --- a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc +++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc @@ -74,3 +74,11 @@ struct InstanceWinsET { constexpr auto vInstanceWinsET = create(InstanceWinsET{}); // { dg-error "is not a constant expression" } +constexpr auto func = [](std::string s) noexcept -> int +{ return s.size(); }; + +constexpr std::function_ref<int(const std::string&)> incompatible1FRef + = std::function_ref<int(std::string)>(func); // { dg-error "is not a constant expression" } +constexpr std::function_ref<long(std::string)> incompatible2FRef + = std::function_ref<int(std::string)>(func); // { dg-error "is not a constant expression" } +
