diff --git a/SG14/inplace_function.h b/SG14/inplace_function.h index cddd81ee..0ccf26b0 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< @@ -190,15 +223,13 @@ class inplace_function template< typename T, typename C = std::decay_t, - typename = std::enable_if_t::value> + typename = std::enable_if_t< + !inplace_function_detail::is_inplace_function::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 a55bee35..42009876 100644 --- a/SG14_test/inplace_function_test.cpp +++ b/SG14_test/inplace_function_test.cpp @@ -489,6 +489,104 @@ static void test_return_by_move() assert(InstrumentedCopyConstructor::moves == 1); } +static int overloaded_function(stdext::inplace_function&& exec) +{ + return exec(); +} + +static int overloaded_function(stdext::inplace_function&& exec) +{ + return exec(42); +} + +static void test_is_invocable() +{ + 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, ""); + static_assert(! is_invocable_r::value, ""); + static_assert(! is_invocable_r::value, ""); + + static_assert(is_invocable_r::value, ""); + static_assert(! is_invocable_r::value, ""); + static_assert(! is_invocable_r::value, ""); + + static_assert(is_invocable_r::value, ""); + static_assert(! is_invocable_r::value, ""); + + // Testing widening and narrowing conversions, and the "conversion" to void. + static_assert(is_invocable_r::value, ""); + static_assert(is_invocable_r::value, ""); + static_assert(is_invocable_r::value, ""); + + // Testing the conversion from void to int, which should definitely not be allowed. + static_assert(! is_invocable_r::value, ""); + + // 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. + static_assert(is_invocable_r::value, ""); + static_assert(is_invocable_r::value, ""); + + // Regression tests for both is_invocable and is_convertible. + static_assert(is_invocable_r::value, ""); + static_assert(is_invocable_r::value, ""); +} + +static void test_overloading() +{ + struct NoDefaultCtor { + int val; + explicit NoDefaultCtor(int v) : val{v} {} + }; + + 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, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + + const auto b = [](int&) -> void {}; + static_assert(is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + + const auto c = [](int, NoDefaultCtor) -> int { return 3; }; + static_assert(is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + + const auto d = []() -> void {}; + static_assert(is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + + static_assert(is_convertible>::value, ""); + static_assert(is_convertible>::value, ""); + + // Same as a, but not const. + auto e = []() -> int { return 3; }; + static_assert(is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + + // Same as a, but not const and mutable. + auto f = []() mutable -> int { return 3; }; + static_assert(is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); + static_assert(!is_convertible>::value, ""); +} void sg14_test::inplace_function_test() { @@ -576,6 +674,8 @@ void sg14_test::inplace_function_test() test_move_construction_from_smaller_buffer_is_noexcept(); test_is_convertible(); test_return_by_move(); + test_is_invocable(); + test_overloading(); } #ifdef TEST_MAIN