PR libstdc++/82254 fix std::is_nothrow_invocable_r w.r.t throwing conversions
PR libstdc++/82254 * include/std/type_traits (__is_invocable): Add partial specialization for INVOKE<void> case and remove is_void<R> check from partial specialization for INVOKE<R> case. (__is_nt_invocable_impl): New helper for is_nothrow_invocable_r. (is_nothrow_invocable_r): Use __is_nt_invocable_impl. * testsuite/20_util/is_nothrow_invocable/value.cc: Add tests for conversions that can throw or fail to convert. Use static assert strings to explain negative results. * testsuite/20_util/is_nothrow_invocable/value_ext.cc: Use is_nothrow_constructible in is_nt_invocable_conv. From-SVN: r252977
This commit is contained in:
parent
e32d238855
commit
c4b06e7f1d
|
@ -1,3 +1,17 @@
|
|||
2017-09-19 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/82254
|
||||
* include/std/type_traits (__is_invocable): Add partial specialization
|
||||
for INVOKE<void> case and remove is_void<R> check from partial
|
||||
specialization for INVOKE<R> case.
|
||||
(__is_nt_invocable_impl): New helper for is_nothrow_invocable_r.
|
||||
(is_nothrow_invocable_r): Use __is_nt_invocable_impl.
|
||||
* testsuite/20_util/is_nothrow_invocable/value.cc: Add tests for
|
||||
conversions that can throw or fail to convert. Use static assert
|
||||
strings to explain negative results.
|
||||
* testsuite/20_util/is_nothrow_invocable/value_ext.cc: Use
|
||||
is_nothrow_constructible in is_nt_invocable_conv.
|
||||
|
||||
2017-09-18 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/81468
|
||||
|
|
|
@ -2592,7 +2592,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
template<typename _Result, typename _Ret>
|
||||
struct __is_invocable_impl<_Result, _Ret, __void_t<typename _Result::type>>
|
||||
: __or_<is_void<_Ret>, is_convertible<typename _Result::type, _Ret>>::type
|
||||
: is_convertible<typename _Result::type, _Ret>::type
|
||||
{ };
|
||||
|
||||
template<typename _Result>
|
||||
struct __is_invocable_impl<_Result, void, __void_t<typename _Result::type>>
|
||||
: true_type
|
||||
{ };
|
||||
|
||||
template<typename _Fn, typename... _ArgTypes>
|
||||
|
@ -2691,10 +2696,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
__call_is_nothrow_<_Fn, _ArgTypes...>>::type
|
||||
{ };
|
||||
|
||||
template<typename _Result, typename _Ret, typename = void>
|
||||
struct __is_nt_invocable_impl : false_type { };
|
||||
|
||||
template<typename _Result, typename _Ret>
|
||||
struct __is_nt_invocable_impl<_Result, _Ret,
|
||||
__void_t<typename _Result::type>>
|
||||
: __and_<is_convertible<typename _Result::type, _Ret>,
|
||||
is_nothrow_constructible<_Ret, typename _Result::type>>
|
||||
{ };
|
||||
|
||||
template<typename _Result>
|
||||
struct __is_nt_invocable_impl<_Result, void,
|
||||
__void_t<typename _Result::type>>
|
||||
: true_type
|
||||
{ };
|
||||
|
||||
/// std::is_nothrow_invocable_r
|
||||
template<typename _Ret, typename _Fn, typename... _ArgTypes>
|
||||
struct is_nothrow_invocable_r
|
||||
: __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, _Ret>,
|
||||
: __and_<__is_nt_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, _Ret>,
|
||||
__call_is_nothrow_<_Fn, _ArgTypes...>>::type
|
||||
{ };
|
||||
|
||||
|
|
|
@ -40,6 +40,10 @@ template<typename R, typename... T>
|
|||
|
||||
void test01()
|
||||
{
|
||||
struct T { T(int) { } };
|
||||
struct NT { NT(int) noexcept { } };
|
||||
struct Ex { explicit Ex(int) noexcept { } };
|
||||
|
||||
using func_type = void(*)();
|
||||
static_assert( ! is_nt_invocable< func_type>(), "");
|
||||
|
||||
|
@ -55,28 +59,46 @@ void test01()
|
|||
static_assert( ! is_nt_invocable< mem_type, int >(), "");
|
||||
static_assert( ! is_nt_invocable< mem_type, int& >(), "");
|
||||
|
||||
static_assert( is_nt_invocable< mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< int, mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< int&, mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< long, mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< int&, mem_type, X* >(), "");
|
||||
static_assert( is_nt_invocable< mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< int, mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< int&, mem_type, X& >(), "");
|
||||
static_assert( is_nt_invocable_r< long, mem_type, X& >(), "");
|
||||
static_assert( ! is_nt_invocable_r< long&, mem_type, X& >(),
|
||||
"conversion fails, cannot bind long& to int");
|
||||
static_assert( is_nt_invocable_r< int&, mem_type, X* >(), "");
|
||||
|
||||
static_assert( ! is_nt_invocable_r< T, mem_type, X& >(),
|
||||
"conversion throws");
|
||||
static_assert( is_nt_invocable_r< NT, mem_type, X& >(), "");
|
||||
static_assert( ! is_nt_invocable_r< Ex, mem_type, X& >(),
|
||||
"conversion fails, would use explicit constructor");
|
||||
|
||||
using memfun_type = int (X::*)();
|
||||
|
||||
static_assert( ! is_nt_invocable< memfun_type >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type, int >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type, int& >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type, X& >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type, X* >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type >(), "no object");
|
||||
static_assert( ! is_nt_invocable< memfun_type, int >(), "no object");
|
||||
static_assert( ! is_nt_invocable< memfun_type, int& >(), "no object");
|
||||
static_assert( ! is_nt_invocable< memfun_type, X& >(), "call throws");
|
||||
static_assert( ! is_nt_invocable< memfun_type, X* >(), "call throws");
|
||||
|
||||
static_assert( ! is_nt_invocable_r< T, memfun_type, X& >(), "call throws");
|
||||
static_assert( ! is_nt_invocable_r< NT, memfun_type, X& >(), "call throws");
|
||||
static_assert( ! is_nt_invocable_r< Ex, memfun_type, X& >(), "call throws");
|
||||
|
||||
#if __cpp_noexcept_function_type
|
||||
using memfun_type_nt = int (X::*)() noexcept;
|
||||
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt, int >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt, int& >(), "");
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt >(), "no object");
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt, int >(), "no object");
|
||||
static_assert( ! is_nt_invocable< memfun_type_nt, int& >(), "no object");
|
||||
static_assert( is_nt_invocable< memfun_type_nt, X& >(), "");
|
||||
static_assert( is_nt_invocable< memfun_type_nt, X* >(), "");
|
||||
|
||||
static_assert( ! is_nt_invocable_r< T, memfun_type_nt, X& >(),
|
||||
"conversion throws");
|
||||
static_assert( is_nt_invocable_r< NT, memfun_type_nt, X& >(), "");
|
||||
static_assert( ! is_nt_invocable_r< Ex, memfun_type_nt, X& >(),
|
||||
"conversion fails, would use explicit constructor");
|
||||
#endif
|
||||
|
||||
struct F {
|
||||
|
@ -89,12 +111,44 @@ void test01()
|
|||
};
|
||||
using CF = const F;
|
||||
|
||||
static_assert( ! is_nt_invocable_r< int&, F >(), "");
|
||||
static_assert( is_nt_invocable_r< long&, CF >(), "");
|
||||
static_assert( ! is_nt_invocable_r< short&, F, int >(), "" );
|
||||
static_assert( is_nt_invocable_r< char&, F&, int >(), "" );
|
||||
static_assert( is_nt_invocable_r< char&, CF, int >(), "" );
|
||||
static_assert( is_nt_invocable_r< char&, CF&, int >(), "" );
|
||||
static_assert( ! is_nt_invocable< F >(), "call throws");
|
||||
static_assert( is_nt_invocable< CF >(), "");
|
||||
|
||||
static_assert( ! is_nt_invocable< F, int, int >(), "");
|
||||
static_assert( ! is_nt_invocable_r< int&, F >(), "call throws");
|
||||
static_assert( is_nt_invocable_r< long&, CF >(), "");
|
||||
static_assert( ! is_nt_invocable_r< T, F >(), "call throws");
|
||||
static_assert( ! is_nt_invocable_r< NT, F >(), "call throws");
|
||||
static_assert( ! is_nt_invocable_r< Ex, F >(), "call throws");
|
||||
static_assert( ! is_nt_invocable_r< T, CF >(), "conversion throws");
|
||||
static_assert( is_nt_invocable_r< NT, CF >(), "" );
|
||||
static_assert( ! is_nt_invocable_r< Ex, CF >(), "conversion fails");
|
||||
|
||||
static_assert( ! is_nt_invocable< F, int >(), "call throws");
|
||||
static_assert( is_nt_invocable< F&, int >(), "");
|
||||
|
||||
static_assert( ! is_nt_invocable_r< short&, F, int >(),
|
||||
"call throws" );
|
||||
static_assert( is_nt_invocable_r< char&, F&, int >(), "");
|
||||
static_assert( ! is_nt_invocable_r< T, F&, int >(),
|
||||
"conversion throws");
|
||||
static_assert( is_nt_invocable_r< NT, F&, int >(), "");
|
||||
static_assert( ! is_nt_invocable_r< Ex, F&, int >(),
|
||||
"conversion fails, would use explicit constructor");
|
||||
|
||||
static_assert( is_nt_invocable< CF, int >(), "");
|
||||
static_assert( is_nt_invocable< CF&, int >(), "");
|
||||
|
||||
static_assert( is_nt_invocable_r< char&, CF, int >(), "");
|
||||
static_assert( is_nt_invocable_r< char&, CF&, int >(), "");
|
||||
|
||||
static_assert( ! is_nt_invocable_r< T, CF&, int >(),
|
||||
"conversion throws");
|
||||
static_assert( is_nt_invocable_r< NT, CF&, int >(), "");
|
||||
static_assert( ! is_nt_invocable_r< Ex, CF&, int >(),
|
||||
"conversion fails, would use explicit constructor");
|
||||
|
||||
static_assert( ! is_nt_invocable< F, int, int >(),
|
||||
"would call private member");
|
||||
static_assert( ! is_nt_invocable_r<void, F, int, int >(),
|
||||
"would call private member");
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ template<typename... T>
|
|||
constexpr bool is_nt_invocable_conv(std::true_type)
|
||||
{
|
||||
using result_type = typename std::__invoke_result<T...>::type;
|
||||
return std::is_void<R>::value || std::is_convertible<result_type, R>::value;
|
||||
return std::is_void<R>::value
|
||||
|| (std::is_convertible<result_type, R>::value
|
||||
&& std::is_nothrow_constructible<R, result_type>::value);
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
|
|
Loading…
Reference in New Issue