From 0ab0cdcb905c406c5582d69df08ba01ea4d43cf2 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 09:13:43 +0100 Subject: [PATCH 01/26] Implement invoke_r --- stl/inc/functional | 11 ++++ stl/inc/yvals_core.h | 2 + tests/std/test.lst | 1 + tests/std/tests/P2136R3_invoke_r/env.lst | 4 ++ tests/std/tests/P2136R3_invoke_r/test.cpp | 50 +++++++++++++++++++ .../test.compile.pass.cpp | 10 ++++ 6 files changed, 78 insertions(+) create mode 100644 tests/std/tests/P2136R3_invoke_r/env.lst create mode 100644 tests/std/tests/P2136R3_invoke_r/test.cpp diff --git a/stl/inc/functional b/stl/inc/functional index 649ab6d2a97..071129fbd36 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -29,6 +29,17 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN +#if _HAS_CXX23 +// clang-format off +template +requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> +constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) +noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...>) { + return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); +} +// clang-format on +#endif // _HAS_CXX23 + // STRUCT TEMPLATE plus // defined in diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 66a66b4464a..efaa9a1b50b 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -245,6 +245,7 @@ // P2102R0 Making "Implicit Expression Variations" More Explicit // P2106R0 Range Algorithm Result Types // P2116R0 Removing tuple-Like Protocol Support From Fixed-Extent span +// P2136R3 invoke_r // P????R? directory_entry::clear_cache() // _HAS_CXX20 indirectly controls: @@ -1340,6 +1341,7 @@ // C++23 #if _HAS_CXX23 +#define __cpp_lib_invoke_r 202106L #define __cpp_lib_is_scoped_enum 202011L #endif // _HAS_CXX23 diff --git a/tests/std/test.lst b/tests/std/test.lst index cba3615d387..d7d3ae05972 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -419,6 +419,7 @@ tests\P1423R3_char8_t_remediation tests\P1502R1_standard_library_header_units tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric +tests\P2136R3_invoke_r tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions tests\VSO_0000000_c_math_functions diff --git a/tests/std/tests/P2136R3_invoke_r/env.lst b/tests/std/tests/P2136R3_invoke_r/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P2136R3_invoke_r/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp new file mode 100644 index 00000000000..86a47d9ee58 --- /dev/null +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +constexpr int square(int n) { + return n * n; +} +constexpr int square_noexcept(int n) noexcept { + return n * n; +} + +struct Thing { + int n = 0; + constexpr int& foo() { + return n; + } +}; + +constexpr bool is_rvalue(const int&) { + return false; +} +constexpr bool is_rvalue(int&&) { + return true; +} + +constexpr bool test_invoke_r() { + auto v = invoke_r(square, 3); + assert(v == 9L); + assert((is_same_v) ); + +#ifdef __cpp_noexcept_function_type + static_assert(!noexcept(invoke_r(square, 3))); + static_assert(noexcept(invoke_r(square_noexcept, 3))); +#endif + + assert(!is_rvalue(invoke(&Thing::foo, Thing{}))); + assert(is_rvalue(invoke_r(&Thing::foo, Thing{}))); + + return true; +} + +int main() { + test_invoke_r(); + static_assert(test_invoke_r()); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 0da859a6bf9..969a69d1912 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -847,6 +847,16 @@ STATIC_ASSERT(__cpp_lib_interpolate == 201902L); STATIC_ASSERT(__cpp_lib_invoke == 201411L); #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_invoke_r +#error __cpp_lib_invoke_r is not defined +#elif __cpp_lib_invoke_r != 202106L +#error __cpp_lib_invoke_r is not 202106L +#else +STATIC_ASSERT(__cpp_lib_invoke_r == 202106L); +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_is_aggregate #error __cpp_lib_is_aggregate is not defined From 4ef0e40e72f5229ed16c46ac64d4a414ca7c4cd3 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 09:35:09 +0100 Subject: [PATCH 02/26] trailing spaces --- stl/inc/functional | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 071129fbd36..ebeb72c0142 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -31,9 +31,9 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 // clang-format off -template -requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> -constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) +template +requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> +constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...>) { return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); } From 88caed6e06e2d273fc879eea898a8f731dbf4849 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 13:02:12 +0100 Subject: [PATCH 03/26] if constexpr for void Co-authored-by: frederick-vs-ja --- stl/inc/functional | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index ebeb72c0142..2671e3819a2 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -31,11 +31,15 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 // clang-format off -template -requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> -constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) -noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...>) { - return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); +template + requires is_invocable_r_v<_Result_type, _Callable, _Types...> +constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { + if constexpr (is_void_v<_Result_type>) { + (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + } else { + return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + } } // clang-format on #endif // _HAS_CXX23 From c0d78ba5e2ecc73738eaa2e8a8427a96331f4cdf Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 13:12:09 +0100 Subject: [PATCH 04/26] Add zero argument and void return type functionality. --- stl/inc/functional | 10 ++++++++++ tests/std/tests/P2136R3_invoke_r/test.cpp | 12 +++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 2671e3819a2..8d2c749ef4c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -30,6 +30,16 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 +template +requires is_invocable_r_v<_Result_type, _Callable> constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable>) { + if constexpr (is_void_v<_Result_type>) { + (void) _STD invoke(_STD forward<_Callable>(_Obj)); + } else { + return _STD invoke(_STD forward<_Callable>(_Obj)); + } +} + // clang-format off template requires is_invocable_r_v<_Result_type, _Callable, _Types...> diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 86a47d9ee58..cceb5dfd5b0 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -29,9 +29,15 @@ constexpr bool is_rvalue(int&&) { } constexpr bool test_invoke_r() { - auto v = invoke_r(square, 3); - assert(v == 9L); - assert((is_same_v) ); + auto v1 = invoke_r(square, 3); + assert(v1 == 9L); + assert((is_same_v) ); + + auto v2 = invoke_r([]() -> int { return 5; }); + assert(v2 == 5); + assert((is_same_v) ); + + static_assert(is_void_v(square, 1))>); #ifdef __cpp_noexcept_function_type static_assert(!noexcept(invoke_r(square, 3))); From f376138aea7e6b4b996bd3158230776f8980511f Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 13:15:31 +0100 Subject: [PATCH 05/26] [skip ci] formatting --- stl/inc/functional | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 8d2c749ef4c..01a4b8001c9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -30,9 +30,10 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 +// clang-format off template -requires is_invocable_r_v<_Result_type, _Callable> constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept( - is_nothrow_invocable_r_v<_Result_type, _Callable>) { +requires is_invocable_r_v<_Result_type, _Callable> +constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable>) { if constexpr (is_void_v<_Result_type>) { (void) _STD invoke(_STD forward<_Callable>(_Obj)); } else { @@ -40,9 +41,8 @@ requires is_invocable_r_v<_Result_type, _Callable> constexpr _Result_type invoke } } -// clang-format off template - requires is_invocable_r_v<_Result_type, _Callable, _Types...> +requires is_invocable_r_v<_Result_type, _Callable, _Types...> constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { if constexpr (is_void_v<_Result_type>) { From 1c53570365360909e8f87c3eae675dae9da81c20 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sun, 20 Jun 2021 16:37:58 +0100 Subject: [PATCH 06/26] Add _NODISCARD --- stl/inc/functional | 4 ++-- tests/std/tests/P2136R3_invoke_r/test.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 01a4b8001c9..0720dda53b5 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -33,7 +33,7 @@ _STD_BEGIN // clang-format off template requires is_invocable_r_v<_Result_type, _Callable> -constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable>) { +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable>) { if constexpr (is_void_v<_Result_type>) { (void) _STD invoke(_STD forward<_Callable>(_Obj)); } else { @@ -43,7 +43,7 @@ constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept(is_nothrow_invocable_ template requires is_invocable_r_v<_Result_type, _Callable, _Types...> -constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { if constexpr (is_void_v<_Result_type>) { (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index cceb5dfd5b0..aaf60bfeb73 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -38,9 +38,10 @@ constexpr bool test_invoke_r() { assert((is_same_v) ); static_assert(is_void_v(square, 1))>); + invoke_r([] { return 1; }); // no nodiscard warning #ifdef __cpp_noexcept_function_type - static_assert(!noexcept(invoke_r(square, 3))); + static_assert(!noexcept(invoke_r(square, 3))); // fails /permissive static_assert(noexcept(invoke_r(square_noexcept, 3))); #endif From 4a91928e58e3a5776f93a53f51d9d48d9f5ffc16 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Mon, 21 Jun 2021 14:41:02 +0100 Subject: [PATCH 07/26] [skip ci] correct comment placement and `()` --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index efaa9a1b50b..b411f57d950 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -245,7 +245,6 @@ // P2102R0 Making "Implicit Expression Variations" More Explicit // P2106R0 Range Algorithm Result Types // P2116R0 Removing tuple-Like Protocol Support From Fixed-Extent span -// P2136R3 invoke_r // P????R? directory_entry::clear_cache() // _HAS_CXX20 indirectly controls: @@ -258,6 +257,7 @@ // _HAS_CXX23 directly controls: // P1048R1 is_scoped_enum +// P2136R3 invoke_r() // Parallel Algorithms Notes // C++ allows an implementation to implement parallel algorithms as calls to the serial algorithms. From 9a30dfd4eeab46b1c3e4dff2ec0bbb26f16e33fd Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Tue, 22 Jun 2021 06:57:17 +0100 Subject: [PATCH 08/26] [skip ci] formatting --- stl/inc/functional | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 0720dda53b5..04c8cc230c2 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -32,8 +32,9 @@ _STD_BEGIN #if _HAS_CXX23 // clang-format off template -requires is_invocable_r_v<_Result_type, _Callable> -_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept(is_nothrow_invocable_r_v<_Result_type, _Callable>) { +requires is_invocable_r_v<_Result_type, _Callable> +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable>) { if constexpr (is_void_v<_Result_type>) { (void) _STD invoke(_STD forward<_Callable>(_Obj)); } else { From 6b1dde457a53cbe2f3ffa6e323c6395bdd3c2fd6 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Tue, 22 Jun 2021 08:49:56 +0100 Subject: [PATCH 09/26] Restore 3 arg overload. --- stl/inc/functional | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 04c8cc230c2..9ce4b476b70 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -42,14 +42,14 @@ _NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept( } } -template -requires is_invocable_r_v<_Result_type, _Callable, _Types...> -_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( - is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { +template +requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...>) { if constexpr (is_void_v<_Result_type>) { - (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); } else { - return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); } } // clang-format on From 059fdbaa08c1ecfe7946ea377012fae94bb382f5 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 23 Jun 2021 12:19:54 +0100 Subject: [PATCH 10/26] Use invoke_r on data member and use is_permissive trick. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 35 +++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index aaf60bfeb73..4789b80f24d 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -7,6 +7,28 @@ using namespace std; +// TRANSITION, DevCom-1457457 +namespace detail { + static constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} // namespace detail +constexpr bool is_permissive = detail::Derived::test(); + constexpr int square(int n) { return n * n; } @@ -38,15 +60,18 @@ constexpr bool test_invoke_r() { assert((is_same_v) ); static_assert(is_void_v(square, 1))>); - invoke_r([] { return 1; }); // no nodiscard warning + Thing thing; + invoke_r(&Thing::n, thing); // no nodiscard warning + // TRANSITION, DevCom-1457457 + static_assert(!noexcept(invoke_r(square, 3)) == !is_permissive, "invoke_r(square, 3) is noexcept"); + static_assert(!noexcept(invoke(square, 3)) == !is_permissive, "invoke(square, 3) is noexcept"); #ifdef __cpp_noexcept_function_type - static_assert(!noexcept(invoke_r(square, 3))); // fails /permissive - static_assert(noexcept(invoke_r(square_noexcept, 3))); + static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); #endif - assert(!is_rvalue(invoke(&Thing::foo, Thing{}))); - assert(is_rvalue(invoke_r(&Thing::foo, Thing{}))); + assert(!is_rvalue(invoke(&Thing::foo, thing))); + assert(is_rvalue(invoke_r(&Thing::foo, thing))); return true; } From 1963a65bbbc21ee4306ce25bc9346193df954d2a Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 23 Jun 2021 12:29:52 +0100 Subject: [PATCH 11/26] Make a templated noexcept_test (probably should have tested this less confusing version first) --- tests/std/tests/P2136R3_invoke_r/test.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 4789b80f24d..4a93f68c46b 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -50,6 +50,12 @@ constexpr bool is_rvalue(int&&) { return true; } +template +constexpr void noexcept_test() { + static_assert(!noexcept(invoke_r(square, 3)), "invoke_r(square, 3) is noexcept"); + static_assert(!noexcept(invoke(square, 3)), "invoke(square, 3) is noexcept"); +} + constexpr bool test_invoke_r() { auto v1 = invoke_r(square, 3); assert(v1 == 9L); @@ -64,8 +70,9 @@ constexpr bool test_invoke_r() { invoke_r(&Thing::n, thing); // no nodiscard warning // TRANSITION, DevCom-1457457 - static_assert(!noexcept(invoke_r(square, 3)) == !is_permissive, "invoke_r(square, 3) is noexcept"); - static_assert(!noexcept(invoke(square, 3)) == !is_permissive, "invoke(square, 3) is noexcept"); + if constexpr (!is_permissive) { + noexcept_test(); + } #ifdef __cpp_noexcept_function_type static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); #endif From 78abdc794be9b3943c2ff9884538315e31bbf75c Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 30 Jun 2021 17:16:02 +0100 Subject: [PATCH 12/26] Small changes * Use static_assert for type_traits * Test against is_permissive instead of function template * Reorganise tests slightly --- tests/std/tests/P2136R3_invoke_r/test.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 4a93f68c46b..f0126cd2bde 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -50,33 +50,26 @@ constexpr bool is_rvalue(int&&) { return true; } -template -constexpr void noexcept_test() { - static_assert(!noexcept(invoke_r(square, 3)), "invoke_r(square, 3) is noexcept"); - static_assert(!noexcept(invoke(square, 3)), "invoke(square, 3) is noexcept"); -} - constexpr bool test_invoke_r() { auto v1 = invoke_r(square, 3); assert(v1 == 9L); - assert((is_same_v) ); + static_assert(is_same_v); auto v2 = invoke_r([]() -> int { return 5; }); assert(v2 == 5); - assert((is_same_v) ); - + static_assert(is_same_v); static_assert(is_void_v(square, 1))>); - Thing thing; - invoke_r(&Thing::n, thing); // no nodiscard warning // TRANSITION, DevCom-1457457 - if constexpr (!is_permissive) { - noexcept_test(); - } + static_assert(noexcept(invoke_r(square, 3)) == is_permissive, "invoke_r(square, 3) is noexcept"); + static_assert(noexcept(invoke(square, 3)) == is_permissive, "invoke(square, 3) is noexcept"); #ifdef __cpp_noexcept_function_type static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); + static_assert(noexcept(invoke(square_noexcept, 3)), "invoke(square_noexcept, 3) isn't noexcept"); #endif + Thing thing; + invoke_r(&Thing::n, thing); // no nodiscard warning assert(!is_rvalue(invoke(&Thing::foo, thing))); assert(is_rvalue(invoke_r(&Thing::foo, thing))); From 27ecd63e91da76fe3bd725918fda61b7854f9f36 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Thu, 1 Jul 2021 07:12:34 +0100 Subject: [PATCH 13/26] [skip ci] SHOUTY banner --- stl/inc/functional | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/functional b/stl/inc/functional index 9ce4b476b70..86de4f63d6d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -30,6 +30,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 +// FUNCTION TEMPLATE invoke_r // clang-format off template requires is_invocable_r_v<_Result_type, _Callable> From edc4e1563255e9fbf5358c76e495b467e0e23a0c Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Thu, 1 Jul 2021 16:19:05 +0100 Subject: [PATCH 14/26] Use single overload Co-authored-by: statementreply --- stl/inc/functional | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 86de4f63d6d..18057cda5c9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -32,25 +32,14 @@ _STD_BEGIN #if _HAS_CXX23 // FUNCTION TEMPLATE invoke_r // clang-format off -template -requires is_invocable_r_v<_Result_type, _Callable> -_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj) noexcept( - is_nothrow_invocable_r_v<_Result_type, _Callable>) { +template +requires is_invocable_r_v<_Result_type, _Callable, _Types...> +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { if constexpr (is_void_v<_Result_type>) { - (void) _STD invoke(_STD forward<_Callable>(_Obj)); + (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); } else { - return _STD invoke(_STD forward<_Callable>(_Obj)); - } -} - -template -requires is_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...> -_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) noexcept( - is_nothrow_invocable_r_v<_Result_type, _Callable, _Ty1, _Types2...>) { - if constexpr (is_void_v<_Result_type>) { - (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); - } else { - return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Ty1>(_Arg1), _STD forward<_Types2>(_Args2)...); + return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); } } // clang-format on From 554e25ed34f63897dd16d0e5ed82fcc147b3ae6a Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Jul 2021 07:46:55 +0100 Subject: [PATCH 15/26] Use SFINAE instead of requires --- stl/inc/functional | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 18057cda5c9..f46b2978e6e 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -31,9 +31,8 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 // FUNCTION TEMPLATE invoke_r -// clang-format off -template -requires is_invocable_r_v<_Result_type, _Callable, _Types...> +template , int> = 0> _NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { if constexpr (is_void_v<_Result_type>) { @@ -42,7 +41,6 @@ _NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); } } -// clang-format on #endif // _HAS_CXX23 // STRUCT TEMPLATE plus From b5d260646404b678f9d37deb90072384833ad468 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Jul 2021 07:54:19 +0100 Subject: [PATCH 16/26] long int and foo --- tests/std/tests/P2136R3_invoke_r/test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index f0126cd2bde..45289fad77f 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -38,7 +38,7 @@ constexpr int square_noexcept(int n) noexcept { struct Thing { int n = 0; - constexpr int& foo() { + constexpr int& moo() { return n; } }; @@ -51,9 +51,9 @@ constexpr bool is_rvalue(int&&) { } constexpr bool test_invoke_r() { - auto v1 = invoke_r(square, 3); + auto v1 = invoke_r(square, 3); assert(v1 == 9L); - static_assert(is_same_v); + static_assert(is_same_v); auto v2 = invoke_r([]() -> int { return 5; }); assert(v2 == 5); @@ -70,8 +70,8 @@ constexpr bool test_invoke_r() { Thing thing; invoke_r(&Thing::n, thing); // no nodiscard warning - assert(!is_rvalue(invoke(&Thing::foo, thing))); - assert(is_rvalue(invoke_r(&Thing::foo, thing))); + assert(!is_rvalue(invoke(&Thing::moo, thing))); + assert(is_rvalue(invoke_r(&Thing::moo, thing))); return true; } From 64f3d307df30956449b4d52c3a2ec77af9ec9581 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Jul 2021 08:14:58 +0100 Subject: [PATCH 17/26] use traits and add additional noexcept test --- tests/std/tests/P2136R3_invoke_r/test.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 45289fad77f..c361a3a6a9a 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -43,12 +43,7 @@ struct Thing { } }; -constexpr bool is_rvalue(const int&) { - return false; -} -constexpr bool is_rvalue(int&&) { - return true; -} +constexpr const char* cstring() noexcept; constexpr bool test_invoke_r() { auto v1 = invoke_r(square, 3); @@ -64,14 +59,16 @@ constexpr bool test_invoke_r() { static_assert(noexcept(invoke_r(square, 3)) == is_permissive, "invoke_r(square, 3) is noexcept"); static_assert(noexcept(invoke(square, 3)) == is_permissive, "invoke(square, 3) is noexcept"); #ifdef __cpp_noexcept_function_type - static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); static_assert(noexcept(invoke(square_noexcept, 3)), "invoke(square_noexcept, 3) isn't noexcept"); + static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); + static_assert(noexcept(invoke(cstring)), "invoke(cstring) isn't noexcept"); + static_assert(!noexcept(invoke_r(cstring)), "invoke_r(cstring) is noexcept"); #endif Thing thing; invoke_r(&Thing::n, thing); // no nodiscard warning - assert(!is_rvalue(invoke(&Thing::moo, thing))); - assert(is_rvalue(invoke_r(&Thing::moo, thing))); + static_assert(is_same_v); + static_assert(is_same_v(&Thing::moo, thing)), int>); return true; } From 46fa210ebe994ab448b57bdb90a14207ec527cd5 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Jul 2021 08:35:55 +0100 Subject: [PATCH 18/26] additional tests --- tests/std/tests/P2136R3_invoke_r/test.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index c361a3a6a9a..9d5d1b299d3 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -35,6 +35,7 @@ constexpr int square(int n) { constexpr int square_noexcept(int n) noexcept { return n * n; } +constexpr const char* cstring() noexcept; struct Thing { int n = 0; @@ -43,7 +44,14 @@ struct Thing { } }; -constexpr const char* cstring() noexcept; +struct RefQualified { + constexpr int operator()(int) && { + return 1; + } + constexpr int operator()(int) & { + return 2; + } +}; constexpr bool test_invoke_r() { auto v1 = invoke_r(square, 3); @@ -70,6 +78,14 @@ constexpr bool test_invoke_r() { static_assert(is_same_v); static_assert(is_same_v(&Thing::moo, thing)), int>); + int count = 0; + assert(invoke_r([&count] { return ++count; }) == 1); + assert(count == 1); + + assert(invoke_r(RefQualified{}, 0) == 1); + RefQualified r; + assert(invoke_r(r, 0) == 2); + return true; } From e169e277c758b9ab9ee58fbb5e944c3342726206 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Jul 2021 09:40:35 +0100 Subject: [PATCH 19/26] Missed argument overloads --- tests/std/tests/P2136R3_invoke_r/test.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 9d5d1b299d3..b9e76dec993 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -45,12 +45,18 @@ struct Thing { }; struct RefQualified { - constexpr int operator()(int) && { + constexpr int operator()(int&&) && { return 1; } - constexpr int operator()(int) & { + constexpr int operator()(const int&) && { return 2; } + constexpr int operator()(int&&) & { + return 3; + } + constexpr int operator()(const int&) & { + return 4; + } }; constexpr bool test_invoke_r() { @@ -83,8 +89,10 @@ constexpr bool test_invoke_r() { assert(count == 1); assert(invoke_r(RefQualified{}, 0) == 1); + assert(invoke_r(RefQualified{}, count) == 2); RefQualified r; - assert(invoke_r(r, 0) == 2); + assert(invoke_r(r, 0) == 3); + assert(invoke_r(r, count) == 4); return true; } From b197c7d24c6a2126c22387f03a3f5e2c0c967462 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 14 Jul 2021 08:38:54 +0100 Subject: [PATCH 20/26] Test noexcept functions are false if __cpp_noexcept_function_type isn't defined. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index b9e76dec993..2ec74932a8f 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -72,12 +72,19 @@ constexpr bool test_invoke_r() { // TRANSITION, DevCom-1457457 static_assert(noexcept(invoke_r(square, 3)) == is_permissive, "invoke_r(square, 3) is noexcept"); static_assert(noexcept(invoke(square, 3)) == is_permissive, "invoke(square, 3) is noexcept"); + + constexpr bool has_noexcept_in_type = #ifdef __cpp_noexcept_function_type - static_assert(noexcept(invoke(square_noexcept, 3)), "invoke(square_noexcept, 3) isn't noexcept"); - static_assert(noexcept(invoke_r(square_noexcept, 3)), "invoke_r(square_noexcept, 3) isn't noexcept"); - static_assert(noexcept(invoke(cstring)), "invoke(cstring) isn't noexcept"); - static_assert(!noexcept(invoke_r(cstring)), "invoke_r(cstring) is noexcept"); + true; +#else + false; #endif + static_assert( + noexcept(invoke(square_noexcept, 3)) == has_noexcept_in_type, "invoke(square_noexcept, 3) isn't noexcept"); + static_assert(noexcept(invoke_r(square_noexcept, 3)) == has_noexcept_in_type, + "invoke_r(square_noexcept, 3) isn't noexcept"); + static_assert(noexcept(invoke(cstring)) == has_noexcept_in_type, "invoke(cstring) isn't noexcept"); + static_assert(!noexcept(invoke_r(cstring)), "invoke_r(cstring) is noexcept"); Thing thing; invoke_r(&Thing::n, thing); // no nodiscard warning From 45a3d89bd2bca128cd8e89ea4b8cd79daf1e3b04 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 14 Jul 2021 08:49:54 +0100 Subject: [PATCH 21/26] Assert messages are no longer correct. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 2ec74932a8f..9d0af5a04e3 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -5,6 +5,8 @@ #include #include +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + using namespace std; // TRANSITION, DevCom-1457457 @@ -79,12 +81,10 @@ constexpr bool test_invoke_r() { #else false; #endif - static_assert( - noexcept(invoke(square_noexcept, 3)) == has_noexcept_in_type, "invoke(square_noexcept, 3) isn't noexcept"); - static_assert(noexcept(invoke_r(square_noexcept, 3)) == has_noexcept_in_type, - "invoke_r(square_noexcept, 3) isn't noexcept"); - static_assert(noexcept(invoke(cstring)) == has_noexcept_in_type, "invoke(cstring) isn't noexcept"); - static_assert(!noexcept(invoke_r(cstring)), "invoke_r(cstring) is noexcept"); + STATIC_ASSERT(noexcept(invoke(square_noexcept, 3)) == has_noexcept_in_type); + STATIC_ASSERT(noexcept(invoke_r(square_noexcept, 3)) == has_noexcept_in_type); + STATIC_ASSERT(noexcept(invoke(cstring)) == has_noexcept_in_type); + STATIC_ASSERT(!noexcept(invoke_r(cstring))); Thing thing; invoke_r(&Thing::n, thing); // no nodiscard warning From d2018be5823ae3f21d976cba59896e5a88b33b82 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 23 Jul 2021 18:53:41 +0100 Subject: [PATCH 22/26] [skip ci] shhh --- stl/inc/functional | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index e662e27e88c..28607589259 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -30,7 +30,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX23 -// FUNCTION TEMPLATE invoke_r template , int> = 0> _NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( From 56bc89f202aa1438c0e81bd33e8e994d4480781b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 9 Aug 2021 19:20:01 -0700 Subject: [PATCH 23/26] Test a stateful lambda. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index 9d0af5a04e3..f55b1246d74 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -91,15 +91,19 @@ constexpr bool test_invoke_r() { static_assert(is_same_v); static_assert(is_same_v(&Thing::moo, thing)), int>); - int count = 0; - assert(invoke_r([&count] { return ++count; }) == 1); - assert(count == 1); - + auto lambda = [counter = 0]() mutable { return ++counter; }; + assert(lambda() == 1); + assert(lambda() == 2); + assert(invoke_r(lambda) == 3); + assert(invoke_r(lambda) == 4); + assert(lambda() == 5); + + int lvalue = 0; assert(invoke_r(RefQualified{}, 0) == 1); - assert(invoke_r(RefQualified{}, count) == 2); + assert(invoke_r(RefQualified{}, lvalue) == 2); RefQualified r; assert(invoke_r(r, 0) == 3); - assert(invoke_r(r, count) == 4); + assert(invoke_r(r, lvalue) == 4); return true; } From f2ede7207d163eee975729c2e33cfc63107073a0 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 9 Aug 2021 19:30:58 -0700 Subject: [PATCH 24/26] Need to include ``. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index f55b1246d74..a6e7c667ff2 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) From 021209d3301bd97ccec86ad0e4e242a7f58e88c7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 9 Aug 2021 19:36:37 -0700 Subject: [PATCH 25/26] Consistently use `STATIC_ASSERT`. --- tests/std/tests/P2136R3_invoke_r/test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp index a6e7c667ff2..f8d7bac3192 100644 --- a/tests/std/tests/P2136R3_invoke_r/test.cpp +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -65,16 +65,16 @@ struct RefQualified { constexpr bool test_invoke_r() { auto v1 = invoke_r(square, 3); assert(v1 == 9L); - static_assert(is_same_v); + STATIC_ASSERT(is_same_v); auto v2 = invoke_r([]() -> int { return 5; }); assert(v2 == 5); - static_assert(is_same_v); - static_assert(is_void_v(square, 1))>); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_void_v(square, 1))>); // TRANSITION, DevCom-1457457 - static_assert(noexcept(invoke_r(square, 3)) == is_permissive, "invoke_r(square, 3) is noexcept"); - static_assert(noexcept(invoke(square, 3)) == is_permissive, "invoke(square, 3) is noexcept"); + STATIC_ASSERT(noexcept(invoke_r(square, 3)) == is_permissive); + STATIC_ASSERT(noexcept(invoke(square, 3)) == is_permissive); constexpr bool has_noexcept_in_type = #ifdef __cpp_noexcept_function_type @@ -89,8 +89,8 @@ constexpr bool test_invoke_r() { Thing thing; invoke_r(&Thing::n, thing); // no nodiscard warning - static_assert(is_same_v); - static_assert(is_same_v(&Thing::moo, thing)), int>); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v(&Thing::moo, thing)), int>); auto lambda = [counter = 0]() mutable { return ++counter; }; assert(lambda() == 1); @@ -111,5 +111,5 @@ constexpr bool test_invoke_r() { int main() { test_invoke_r(); - static_assert(test_invoke_r()); + STATIC_ASSERT(test_invoke_r()); } From 1ab5964afb183259d8e7bd390efe206c50f696d1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 13 Aug 2021 21:31:04 -0700 Subject: [PATCH 26/26] Use `static_cast` for throughput/consistency. --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 815688bf437..2db14e9137d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -35,9 +35,9 @@ template ) { if constexpr (is_void_v<_Result_type>) { - (void) _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + (void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); } else { - return _STD invoke(_STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...); + return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); } } #endif // _HAS_CXX23