diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -3686,22 +3686,61 @@ template using invoke_result_t = typename invoke_result<_Fn, _Args...>::type; +// is_invocable_r + +// Detects whether an express of type _From can be implicitly converted to _To +// according to [conv]. Note that this is distinct from is_convertible, which +// for non-reference _From types relies on returning an rvalue reference from a +// function and therefore gives the wrong answer for non-moveable types. +// +// REQUIRES: _From is not cv void. +template +struct __is_implicitly_convertible { +private: + template + static void _RequireImplicitlyConvertibleTo(_Tp); + + template + static _Tp _Make(); + + template (_Make<_Tp>()))> + static true_type _TestImplicitConversion(int); + + template + static false_type _TestImplicitConversion(...); + +public: + using type = decltype(_TestImplicitConversion<_From>(0)); + static constexpr bool value = type::value; +}; + +template +struct __is_invocable_r_impl : public false_type {}; + +template +struct __is_invocable_r_impl>, _Ret, _Fn, _Args...> + // By now we know that _Fn is invocable with _Args. If _Ret is cv void, the + // answer is always yes. Otherwise the actual result type must be implicitly + // convertible to _Ret. + : conditional_t< is_same_v, void>, + true_type, // + __is_implicitly_convertible, _Ret>> {}; + +template +struct _LIBCPP_TEMPLATE_VIS is_invocable_r : __is_invocable_r_impl {}; + +template +inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value; + // is_invocable template struct _LIBCPP_TEMPLATE_VIS is_invocable : integral_constant::value> {}; -template -struct _LIBCPP_TEMPLATE_VIS is_invocable_r - : integral_constant::value> {}; - template inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value; -template -inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value; - // is_nothrow_invocable template diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// Tests for is_invocable_r. + +#include + +// Non-invocable types + +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); + +// Result type matches + +template +T Return(); + +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// void result type + +// Any actual return type should be useable with a result type of void. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// const- and volatile-qualified void should work too. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// Conversion of result type + +// It should be possible to use a result type to which the actual return type +// can be converted. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// But not a result type where the conversion doesn't work. +static_assert(!std::is_invocable_r_v)>); +static_assert(!std::is_invocable_r_v)>); + +// Non-moveable result type + +// Define a type that can't be move-constructed. +struct CantMove { + CantMove() = default; + CantMove(CantMove&&) = delete; +}; + +static_assert(!std::is_move_constructible_v); +static_assert(!std::is_copy_constructible_v); + +// Define a function that returns that type. +CantMove MakeCantMove() { return CantMove(); } + +// Assumption check: it should be possible to call that function and use it to +// initialize a CantMove object. +CantMove cant_move = MakeCantMove(); + +// Therefore std::is_invocable_r should agree that it can be invoked to yield a +// CantMove. +static_assert(std::is_invocable_r_v); + +// Of course it still shouldn't be possible to call the function and get back +// some other type. +static_assert(!std::is_invocable_r_v);