diff --git a/SG14/inplace_function.h b/SG14/inplace_function.h index f36d988b..22d4fe4f 100644 --- a/SG14/inplace_function.h +++ b/SG14/inplace_function.h @@ -150,6 +150,39 @@ struct is_valid_inplace_dst : std::true_type ); }; +// C++11 MSVC compatible implementation of std::is_invocable_r. + +template void accept(R); + +template struct is_invocable_r_impl : std::false_type {}; + +template struct is_invocable_r_impl< + decltype(std::declval()(std::declval()...), void()), + void, + F, + Args... +> : std::true_type {}; + +template struct is_invocable_r_impl< + decltype(std::declval()(std::declval()...), void()), + const void, + F, + Args... +> : std::true_type {}; + +template struct is_invocable_r_impl< + decltype(accept(std::declval()(std::declval()...))), + R, + F, + Args... +> : std::true_type {}; + +template using is_invocable_r = is_invocable_r_impl< + void, + R, + F, + Args... +>; } // namespace inplace_function_detail template< @@ -187,15 +220,11 @@ class inplace_function typename = std::enable_if_t< !(std::is_same::value || std::is_convertible::value) + && inplace_function_detail::is_invocable_r::value > > inplace_function(T&& closure) { -#if __cplusplus >= 201703L - static_assert(std::is_invocable_r::value, - "inplace_function cannot be constructed from non-callable type" - ); -#endif static_assert(std::is_copy_constructible::value, "inplace_function cannot be constructed from non-copyable type" ); diff --git a/SG14_test/inplace_function_test.cpp b/SG14_test/inplace_function_test.cpp index 3be08814..26d186ea 100644 --- a/SG14_test/inplace_function_test.cpp +++ b/SG14_test/inplace_function_test.cpp @@ -429,6 +429,77 @@ static void RvalueRefParameter() g(std::make_unique(42)); } +struct NoDefaultCtor +{ + int val; + explicit NoDefaultCtor(int v) : val{v} {} +}; + +int overloaded_function(stdext::inplace_function&& exec) +{ + return exec(); +} + +int overloaded_function(stdext::inplace_function&& exec) +{ + return exec(int{42}); +} + +using C_Int1 = int(); +using C_Int2 = int(int); +using C_Void = void(int&); + +using stdext::inplace_function_detail::is_invocable_r; + +static_assert(is_invocable_r::value, "IV A"); +static_assert(! is_invocable_r::value, "IV B"); +static_assert(! is_invocable_r::value, "IV C"); + +static_assert(is_invocable_r::value, "IV D"); +static_assert(! is_invocable_r::value, "IV E"); +static_assert(! is_invocable_r::value, "IV F"); + +static_assert(is_invocable_r::value, "IV G"); +static_assert(! is_invocable_r::value, "IV H"); + +// cppreference: +// > Determines whether Fn can be invoked with the arguments ArgTypes... +// > to yield a result that is convertible to R. +// +// void is treated specially because a functions return value can be ignored. +// So essentially std::is_convertible. +static_assert(is_invocable_r::value, "IV I"); +static_assert(is_invocable_r::value, "IV J"); + +void test_overloading() +{ + EXPECT_EQ(overloaded_function([]() -> int { return 3; }), 3); + EXPECT_EQ(overloaded_function([](int arg) -> int { return arg; }), 42); + + using std::is_convertible; + using stdext::inplace_function; + + const auto a = []() -> int { return 3; }; + static_assert(is_convertible>::value, "A1"); + static_assert(!is_convertible>::value, "A2"); + static_assert(!is_convertible>::value, "A3"); + + const auto b = [](int&) -> void {}; + static_assert(is_convertible>::value, "B1"); + static_assert(!is_convertible>::value, "B2"); + static_assert(!is_convertible>::value, "B3"); + + const auto c = [](int, NoDefaultCtor) -> int { return 3; }; + static_assert(is_convertible>::value, "C1"); + static_assert(!is_convertible>::value, "C2"); + static_assert(!is_convertible>::value, "C3"); + + const auto d = []() -> void {}; + static_assert(is_convertible>::value, "D1"); + static_assert(!is_convertible>::value, "D2"); + static_assert(!is_convertible>::value, "D3"); +} + void sg14_test::inplace_function_test() { // first set of tests (from Optiver) @@ -512,6 +583,7 @@ void sg14_test::inplace_function_test() test_nullptr(); test_overloaded_operator_new(); test_move_construction_is_noexcept(); + test_overloading(); } #ifdef TEST_MAIN