[libcxx-commits] [libcxx] [libc++] Define an internal API for std::invoke and friends (PR #116637)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 18 07:08:15 PST 2024


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/116637

Currently we're using quite different internal names for the `std::invoke` family of type traits. This adds a layer around the current implementation to make it easier to understand when it is used and makes it easier to define multiple implementations of it.


>From 025f937646f190978325c58967ccb02828a78bd2 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 18 Nov 2024 16:06:31 +0100
Subject: [PATCH] [libc++] Define an internal API for std::invoke and friends

---
 libcxx/include/__algorithm/make_projected.h   | 12 +++---
 libcxx/include/__functional/bind.h            | 22 +++++------
 libcxx/include/__functional/function.h        | 11 ++----
 libcxx/include/__functional/hash.h            |  2 +-
 libcxx/include/__functional/mem_fn.h          |  6 +--
 .../include/__functional/reference_wrapper.h  |  2 +-
 libcxx/include/__hash_table                   |  4 +-
 libcxx/include/__tree                         |  2 +-
 libcxx/include/__type_traits/invoke.h         | 38 +++++++++++++++++++
 libcxx/include/__type_traits/result_of.h      |  4 +-
 libcxx/include/future                         |  6 +--
 libcxx/include/unordered_map                  |  4 +-
 libcxx/include/unordered_set                  |  4 +-
 13 files changed, 76 insertions(+), 41 deletions(-)

diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index 22cceb4cb2fb85..e2f2e174768ae1 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -37,16 +37,16 @@ struct _ProjectedPred {
       : __pred(__pred_arg), __proj(__proj_arg) {}
 
   template <class _Tp>
-  typename __invoke_of<_Pred&, decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_Tp>()))>::type
-      _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI
-      operator()(_Tp&& __v) const {
+  __invoke_result_t<_Pred&, decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_Tp>()))> _LIBCPP_CONSTEXPR
+  _LIBCPP_HIDE_FROM_ABI
+  operator()(_Tp&& __v) const {
     return std::__invoke(__pred, std::__invoke(__proj, std::forward<_Tp>(__v)));
   }
 
   template <class _T1, class _T2>
-  typename __invoke_of<_Pred&,
-                       decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T1>())),
-                       decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T2>()))>::type _LIBCPP_CONSTEXPR
+  __invoke_result_t<_Pred&,
+                    decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T1>())),
+                    decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T2>()))> _LIBCPP_CONSTEXPR
   _LIBCPP_HIDE_FROM_ABI
   operator()(_T1&& __lhs, _T2&& __rhs) const {
     return std::__invoke(
diff --git a/libcxx/include/__functional/bind.h b/libcxx/include/__functional/bind.h
index f82c1517249b16..54ec0062932cbf 100644
--- a/libcxx/include/__functional/bind.h
+++ b/libcxx/include/__functional/bind.h
@@ -82,13 +82,13 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __mu(reference_w
 }
 
 template <class _Ti, class... _Uj, size_t... _Indx>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<_Ti&, _Uj...>::type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<_Ti&, _Uj...>
 __mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>) {
   return __ti(std::forward<_Uj>(std::get<_Indx>(__uj))...);
 }
 
 template <class _Ti, class... _Uj, __enable_if_t<is_bind_expression<_Ti>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<_Ti&, _Uj...>::type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<_Ti&, _Uj...>
 __mu(_Ti& __ti, tuple<_Uj...>& __uj) {
   typedef typename __make_tuple_indices<sizeof...(_Uj)>::type __indices;
   return std::__mu_expand(__ti, __uj, __indices());
@@ -130,12 +130,12 @@ struct __mu_return_invokable // false
 
 template <class _Ti, class... _Uj>
 struct __mu_return_invokable<true, _Ti, _Uj...> {
-  typedef typename __invoke_of<_Ti&, _Uj...>::type type;
+  using type = __invoke_result_t<_Ti&, _Uj...>;
 };
 
 template <class _Ti, class... _Uj>
 struct __mu_return_impl<_Ti, false, true, false, tuple<_Uj...> >
-    : public __mu_return_invokable<__invokable<_Ti&, _Uj...>::value, _Ti, _Uj...> {};
+    : public __mu_return_invokable<__is_invocable_v<_Ti&, _Uj...>, _Ti, _Uj...> {};
 
 template <class _Ti, class _TupleUj>
 struct __mu_return_impl<_Ti, false, false, true, _TupleUj> {
@@ -168,12 +168,12 @@ struct __is_valid_bind_return {
 
 template <class _Fp, class... _BoundArgs, class _TupleUj>
 struct __is_valid_bind_return<_Fp, tuple<_BoundArgs...>, _TupleUj> {
-  static const bool value = __invokable<_Fp, typename __mu_return<_BoundArgs, _TupleUj>::type...>::value;
+  static const bool value = __is_invocable_v<_Fp, typename __mu_return<_BoundArgs, _TupleUj>::type...>;
 };
 
 template <class _Fp, class... _BoundArgs, class _TupleUj>
 struct __is_valid_bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj> {
-  static const bool value = __invokable<_Fp, typename __mu_return<const _BoundArgs, _TupleUj>::type...>::value;
+  static const bool value = __is_invocable_v<_Fp, typename __mu_return<const _BoundArgs, _TupleUj>::type...>;
 };
 
 template <class _Fp, class _BoundArgs, class _TupleUj, bool = __is_valid_bind_return<_Fp, _BoundArgs, _TupleUj>::value>
@@ -181,12 +181,12 @@ struct __bind_return;
 
 template <class _Fp, class... _BoundArgs, class _TupleUj>
 struct __bind_return<_Fp, tuple<_BoundArgs...>, _TupleUj, true> {
-  typedef typename __invoke_of< _Fp&, typename __mu_return< _BoundArgs, _TupleUj >::type... >::type type;
+  using type = __invoke_result_t< _Fp&, typename __mu_return< _BoundArgs, _TupleUj >::type... >;
 };
 
 template <class _Fp, class... _BoundArgs, class _TupleUj>
 struct __bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj, true> {
-  typedef typename __invoke_of< _Fp&, typename __mu_return< const _BoundArgs, _TupleUj >::type... >::type type;
+  using type = __invoke_result_t< _Fp&, typename __mu_return< const _BoundArgs, _TupleUj >::type... >;
 };
 
 template <class _Fp, class _BoundArgs, size_t... _Indx, class _Args>
@@ -256,8 +256,7 @@ class __bind_r : public __bind<_Fp, _BoundArgs...> {
                         is_void<_Rp>::value,
                     int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 result_type operator()(_Args&&... __args) {
-    typedef __invoke_void_return_wrapper<_Rp> _Invoker;
-    return _Invoker::__call(static_cast<base&>(*this), std::forward<_Args>(__args)...);
+    return std::__invoke_r<_Rp>(static_cast<base&>(*this), std::forward<_Args>(__args)...);
   }
 
   template <class... _Args,
@@ -266,8 +265,7 @@ class __bind_r : public __bind<_Fp, _BoundArgs...> {
                               is_void<_Rp>::value,
                           int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 result_type operator()(_Args&&... __args) const {
-    typedef __invoke_void_return_wrapper<_Rp> _Invoker;
-    return _Invoker::__call(static_cast<base const&>(*this), std::forward<_Args>(__args)...);
+    return std::__invoke_r<_Rp>(static_cast<base const&>(*this), std::forward<_Args>(__args)...);
   }
 };
 
diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 543be05f10f330..5a906f32fb3984 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -166,8 +166,7 @@ class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> {
       : __func_(std::move(__f)), __alloc_(std::move(__a)) {}
 
   _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes&&... __arg) {
-    typedef __invoke_void_return_wrapper<_Rp> _Invoker;
-    return _Invoker::__call(__func_, std::forward<_ArgTypes>(__arg)...);
+    return std::__invoke_r<_Rp>(__func_, std::forward<_ArgTypes>(__arg)...);
   }
 
   _LIBCPP_HIDE_FROM_ABI __alloc_func* __clone() const {
@@ -208,8 +207,7 @@ class __default_alloc_func<_Fp, _Rp(_ArgTypes...)> {
   _LIBCPP_HIDE_FROM_ABI explicit __default_alloc_func(const _Target& __f) : __f_(__f) {}
 
   _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes&&... __arg) {
-    typedef __invoke_void_return_wrapper<_Rp> _Invoker;
-    return _Invoker::__call(__f_, std::forward<_ArgTypes>(__arg)...);
+    return std::__invoke_r<_Rp>(__f_, std::forward<_ArgTypes>(__arg)...);
   }
 
   _LIBCPP_HIDE_FROM_ABI __default_alloc_func* __clone() const {
@@ -834,13 +832,12 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
 
   __func __f_;
 
-  template <class _Fp,
-            bool = _And< _IsNotSame<__remove_cvref_t<_Fp>, function>, __invokable<_Fp, _ArgTypes...> >::value>
+  template <class _Fp, bool = !is_same<__remove_cvref_t<_Fp>, function>::value && __is_invocable_v<_Fp, _ArgTypes...> >
   struct __callable;
   template <class _Fp>
   struct __callable<_Fp, true> {
     static const bool value =
-        is_void<_Rp>::value || __is_core_convertible<typename __invoke_of<_Fp, _ArgTypes...>::type, _Rp>::value;
+        is_void<_Rp>::value || __is_core_convertible<__invoke_result_t<_Fp, _ArgTypes...>, _Rp>::value;
   };
   template <class _Fp>
   struct __callable<_Fp, false> {
diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 1f67b6a8377660..28b2635ab12537 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -522,7 +522,7 @@ template <class _Key, class _Hash>
 using __check_hash_requirements _LIBCPP_NODEBUG =
     integral_constant<bool,
                       is_copy_constructible<_Hash>::value && is_move_constructible<_Hash>::value &&
-                          __invokable_r<size_t, _Hash, _Key const&>::value >;
+                          __is_invocable_r_v<size_t, _Hash, _Key const&> >;
 
 template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
diff --git a/libcxx/include/__functional/mem_fn.h b/libcxx/include/__functional/mem_fn.h
index f246edb334bb14..83390352eda237 100644
--- a/libcxx/include/__functional/mem_fn.h
+++ b/libcxx/include/__functional/mem_fn.h
@@ -36,14 +36,14 @@ class __mem_fn : public __weak_result_type<_Tp> {
 
   // invoke
   template <class... _ArgTypes>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<const _Tp&, _ArgTypes...>::type
-  operator()(_ArgTypes&&... __args) const _NOEXCEPT_(__nothrow_invokable<const _Tp&, _ArgTypes...>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<const _Tp&, _ArgTypes...>
+  operator()(_ArgTypes&&... __args) const _NOEXCEPT_(__is_nothrow_invocable_v<const _Tp&, _ArgTypes...>) {
     return std::__invoke(__f_, std::forward<_ArgTypes>(__args)...);
   }
 };
 
 template <class _Rp, class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __mem_fn<_Rp _Tp::*> mem_fn(_Rp _Tp::*__pm) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __mem_fn<_Rp _Tp::*> mem_fn(_Rp _Tp::* __pm) _NOEXCEPT {
   return __mem_fn<_Rp _Tp::*>(__pm);
 }
 
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index a4a66a50cf84ca..d6cd6428f22db2 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -57,7 +57,7 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
 
   // invoke
   template <class... _ArgTypes>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<type&, _ArgTypes...>::type
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<type&, _ArgTypes...>
   operator()(_ArgTypes&&... __args) const
 #if _LIBCPP_STD_VER >= 17
       // Since is_nothrow_invocable requires C++17 LWG3764 is not backported
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index a0c72f4c205413..e7f7bfa957ec76 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -652,9 +652,9 @@ struct __enforce_unordered_container_requirements {
 
 template <class _Key, class _Hash, class _Equal>
 #ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Equal const&, _Key const&, _Key const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Equal const&, _Key const&, _Key const&>,
                          "the specified comparator type does not provide a viable const call operator")
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Hash const&, _Key const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Hash const&, _Key const&>,
                          "the specified hash functor does not provide a viable const call operator")
 #endif
     typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index f6ef21cdaa5b98..11ac55cd97fdbd 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -878,7 +878,7 @@ private:
 
 template <class _Tp, class _Compare>
 #ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Compare const&, _Tp const&, _Tp const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>,
                          "the specified comparator type does not provide a viable const call operator")
 #endif
 int __diagnose_non_const_comparator();
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index 71db32ae6a3cef..5d64d7cac53080 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -29,6 +29,27 @@
 #  pragma GCC system_header
 #endif
 
+// This file defines the following libc++-internal API (back-ported to C++03):
+//
+// template <class... Args>
+// decltype(auto) __invoke(Args&&... args) noexcept(noexcept(std::invoke(std::forward<Args>(args...)))) {
+//   return std::invoke(std::forward<Args>(args)...);
+// }
+//
+// template <class Ret, class... Args>
+// Ret __invoke_r(Args&&... args) {
+//   return std::invoke_r(std::forward<Args>(args)...);
+// }
+//
+// template <class Ret, class Func, class... Args>
+// inline const bool __is_invocable_r_v = is_invocable_r_v<Ret, Func, Args...>;
+//
+// template <class Func, class... Args>
+// inline const bool __is_nothrow_invocable_v = is_nothrow_invocable_v<Func, Args...>;
+//
+// template <class Func, class... Args>
+// using __invoke_result_t = invoke_result_t<Func, Args...>;
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _DecayedFp>
@@ -225,6 +246,23 @@ struct __invoke_void_return_wrapper<_Ret, true> {
   }
 };
 
+template <class _Func, class... _Args>
+inline const bool __is_invocable_v = __invokable<_Func, _Args...>::value;
+
+template <class _Ret, class _Func, class... _Args>
+inline const bool __is_invocable_r_v = __invokable_r<_Ret, _Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+inline const bool __is_nothrow_invocable_v = __nothrow_invokable<_Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+using __invoke_result_t = typename __invoke_of<_Func, _Args...>::type;
+
+template <class _Ret, class... _Args>
+_Ret __invoke_r(_Args&&... __args) {
+  return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
+}
+
 #if _LIBCPP_STD_VER >= 17
 
 // is_invocable
diff --git a/libcxx/include/__type_traits/result_of.h b/libcxx/include/__type_traits/result_of.h
index 73a19447520663..92b845d8efad50 100644
--- a/libcxx/include/__type_traits/result_of.h
+++ b/libcxx/include/__type_traits/result_of.h
@@ -25,7 +25,9 @@ template <class _Callable>
 class _LIBCPP_DEPRECATED_IN_CXX17 result_of;
 
 template <class _Fp, class... _Args>
-class _LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> : public __invoke_of<_Fp, _Args...> {};
+class _LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> {
+  using type = __invoke_result_t<_Fp, _Args...>;
+};
 
 #  if _LIBCPP_STD_VER >= 14
 template <class _Tp>
diff --git a/libcxx/include/future b/libcxx/include/future
index 9f7c95e542fd60..9ebbd012ff03a2 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -1828,7 +1828,7 @@ class _LIBCPP_HIDDEN __async_func {
   tuple<_Fp, _Args...> __f_;
 
 public:
-  typedef typename __invoke_of<_Fp, _Args...>::type _Rp;
+  using _Rp = __invoke_result_t<_Fp, _Args...>;
 
   _LIBCPP_HIDE_FROM_ABI explicit __async_func(_Fp&& __f, _Args&&... __args)
       : __f_(std::move(__f), std::move(__args)...) {}
@@ -1852,7 +1852,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool __does_policy_contain(launch __policy, launch
 }
 
 template <class _Fp, class... _Args>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI future<typename __invoke_of<__decay_t<_Fp>, __decay_t<_Args>...>::type>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI future<__invoke_result_t<__decay_t<_Fp>, __decay_t<_Args>...> >
 async(launch __policy, _Fp&& __f, _Args&&... __args) {
   typedef __async_func<__decay_t<_Fp>, __decay_t<_Args>...> _BF;
   typedef typename _BF::_Rp _Rp;
@@ -1877,7 +1877,7 @@ async(launch __policy, _Fp&& __f, _Args&&... __args) {
 }
 
 template <class _Fp, class... _Args>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI future<typename __invoke_of<__decay_t<_Fp>, __decay_t<_Args>...>::type>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI future<__invoke_result_t<__decay_t<_Fp>, __decay_t<_Args>...> >
 async(_Fp&& __f, _Args&&... __args) {
   return std::async(launch::any, std::forward<_Fp>(__f), std::forward<_Args>(__args)...);
 }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 05aa01a3b7c308..4d2115d4b82f3c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1839,7 +1839,7 @@ struct __container_traits<unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc> > {
   //  other than the container's hash function from within an insert or emplace function
   //  inserting a single element, the insertion has no effect.
   static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
-      __nothrow_invokable<_Hash, const _Key&>::value;
+      __is_nothrow_invocable_v<_Hash, const _Key&>;
 };
 
 template <class _Key,
@@ -2539,7 +2539,7 @@ struct __container_traits<unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc> >
   //  other than the container's hash function from within an insert or emplace function
   //  inserting a single element, the insertion has no effect.
   static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
-      __nothrow_invokable<_Hash, const _Key&>::value;
+      __is_nothrow_invocable_v<_Hash, const _Key&>;
 };
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 7ab1c651b8c956..dfde1d06ebf948 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -1192,7 +1192,7 @@ struct __container_traits<unordered_set<_Value, _Hash, _Pred, _Alloc> > {
   //  other than the container's hash function from within an insert or emplace function
   //  inserting a single element, the insertion has no effect.
   static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
-      __nothrow_invokable<_Hash, const _Value&>::value;
+      __is_nothrow_invocable_v<_Hash, const _Value&>;
 };
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
@@ -1812,7 +1812,7 @@ struct __container_traits<unordered_multiset<_Value, _Hash, _Pred, _Alloc> > {
   //  other than the container's hash function from within an insert or emplace function
   //  inserting a single element, the insertion has no effect.
   static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
-      __nothrow_invokable<_Hash, const _Value&>::value;
+      __is_nothrow_invocable_v<_Hash, const _Value&>;
 };
 
 _LIBCPP_END_NAMESPACE_STD



More information about the libcxx-commits mailing list