diff --git a/stl/inc/functional b/stl/inc/functional index 1e8877df2d..062888fac4 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1577,7 +1577,8 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { template class _Function_base { public: - using result_type = _Rx; + using result_type = _Rx; + using _Signature_without_cv_ref_noex = _Rx(_Types...); struct _Impl_t { // A per-callable-type structure acting as a virtual function table. // Using vtable emulations gives more flexibility for optimizations and reduces the amount of RTTI data. @@ -2060,6 +2061,19 @@ private: is_constructible_v, initializer_list<_Ux>&, _CTypes...> && _Call::template _Is_callable_from>; + template + friend class move_only_function; + + template + static constexpr bool _Is_move_only_fuction_varying_cv_ref_noex() { + if constexpr (_Is_specialization_v<_Vt, move_only_function>) { + return is_same_v; + } else { + return false; + } + } + public: using typename _Call::result_type; @@ -2079,7 +2093,7 @@ public: using _Vt = decay_t<_Fn>; static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. " "(N4950 [func.wrap.move.ctor]/6)"); - if constexpr (is_same_v<_Vt, function<_Signature...>>) { + if constexpr (is_same_v<_Vt, function>) { this->template _Construct_with_old_fn<_Vt>(_STD forward<_Fn>(_Callable)); } else { if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> @@ -2090,8 +2104,13 @@ public: } } - using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; - this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable)); + if constexpr (_Is_move_only_fuction_varying_cv_ref_noex<_Vt>()) { + _Call::_Checked_move(this->_Data, _Callable._Data); + _Callable._Reset_to_null(); + } else { + using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; + this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable)); + } } } diff --git a/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp b/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp index 1102e74009..62e1a6ed6d 100644 --- a/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp +++ b/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp @@ -69,12 +69,18 @@ struct copy_counter { int count = 0; }; -using fn_type = int(copy_counter); +using fn_type = int(copy_counter); +using fn_type_r = int(copy_counter) &; +using fn_type_c = int(copy_counter) const; + +#ifdef __cpp_noexcept_function_type +using fn_type_nx = int(copy_counter) noexcept; +#endif // defined(__cpp_noexcept_function_type) struct small_callable { const int context = 42; - int operator()(const copy_counter& counter) { + int operator()(const copy_counter& counter) const noexcept { assert(context == 42); return counter.count; } @@ -83,7 +89,7 @@ struct small_callable { struct alignas(128) large_callable { const int context = 1729; - int operator()(const copy_counter& counter) { + int operator()(const copy_counter& counter) const noexcept { assert((reinterpret_cast(this) & 0x7f) == 0); assert(context == 1729); return counter.count; @@ -156,6 +162,22 @@ int main() { alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + // Abominables and noexcept specifier + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + + static_assert(!is_constructible_v, move_only_function>); + static_assert(!is_constructible_v, move_only_function>); + +#ifdef __cpp_noexcept_function_type + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + + static_assert(!is_constructible_v, move_only_function>); +#endif // defined(__cpp_noexcept_function_type) + constexpr bool is_64_bit = sizeof(void*) > 4; // Moves from function to move_only_function @@ -163,6 +185,18 @@ int main() { test_wrapped_call, function, small_callable>(0); alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + // Moves from function to abominable move_only_function + alloc_checker{is_64_bit ? 0 : 1}, + test_wrapped_call, function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + alloc_checker{is_64_bit ? 0 : 1}, + test_wrapped_call, function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + +#ifdef __cpp_noexcept_function_type + static_assert(!is_constructible_v, function>); +#endif // defined(__cpp_noexcept_function_type) + alloc_checker{is_64_bit ? 0 : 1}, test_wrapped_copy_call, function, small_callable>(0); alloc_checker{2}, test_wrapped_copy_call, function, large_callable>(0);