[libcxx-commits] [libcxx] [libc++] Simplify the implementation of std::make_from_tuple (PR #215067)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 10 02:01:44 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/215067

>From 5e3b9a52112ff0ca0e9350c48bc51c3f991ba031 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 9 Aug 2026 09:09:12 +0200
Subject: [PATCH] [libc++] Simplify the implementation of std::make_from_tuple

---
 libcxx/include/tuple                          | 77 +++++++------------
 .../tuple.apply/make_from_tuple.pass.cpp      | 39 ----------
 2 files changed, 27 insertions(+), 89 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 2f0127bc7388d..085b19545840c 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -1362,79 +1362,56 @@ struct uses_allocator<tuple<_Tp...>, _Alloc> : true_type {};
 
 #    if _LIBCPP_STD_VER >= 17
 #      define _LIBCPP_NOEXCEPT_RETURN(...)                                                                             \
-        noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; }
+        noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; }                                                        \
+        static_assert(true)
 
-// The _LIBCPP_NOEXCEPT_RETURN macro breaks formatting.
-// clang-format off
 template <class _Fn, class _Tuple, size_t... _Id>
-inline _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto)
-__apply_tuple_impl(_Fn&& __f, _Tuple&& __t, index_sequence<_Id...>)
-    _LIBCPP_NOEXCEPT_RETURN(std::__invoke(std::forward<_Fn>(__f), std::get<_Id>(std::forward<_Tuple>(__t))...))
+inline
+    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __apply_tuple_impl(_Fn&& __f, _Tuple&& __t, index_sequence<_Id...>)
+        _LIBCPP_NOEXCEPT_RETURN(std::__invoke(std::forward<_Fn>(__f), std::get<_Id>(std::forward<_Tuple>(__t))...));
 
 template <class _Fn, class _Tuple>
 inline _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) apply(_Fn&& __f, _Tuple&& __t)
-    _LIBCPP_NOEXCEPT_RETURN(std::__apply_tuple_impl(
-        std::forward<_Fn>(__f),
-        std::forward<_Tuple>(__t),
-        make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>()))
+    _LIBCPP_NOEXCEPT_RETURN(std::__apply_tuple_impl(std::forward<_Fn>(__f),
+                                                    std::forward<_Tuple>(__t),
+                                                    make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>()));
+#      undef _LIBCPP_NOEXCEPT_RETURN
+
+template <class _Tp, class _Tuple, class _Seq = make_index_sequence<tuple_size_v<__remove_cvref_t<_Tuple>>>>
+inline constexpr bool __can_make_from_tuple_v = false;
 
-#if _LIBCPP_STD_VER >= 20
-template <class _Tp, class _Tuple, size_t... _Idx>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>)
-  noexcept(noexcept(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...)))
-  requires is_constructible_v<_Tp, decltype(std::get<_Idx>(std::forward<_Tuple>(__t)))...> {
-  return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...);
-}
-#else
 template <class _Tp, class _Tuple, size_t... _Idx>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>,
-    enable_if_t<is_constructible_v<_Tp, decltype(std::get<_Idx>(std::forward<_Tuple>(__t)))...>> * = nullptr)
-    _LIBCPP_NOEXCEPT_RETURN(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...))
-#endif // _LIBCPP_STD_VER >= 20
-#undef _LIBCPP_NOEXCEPT_RETURN
+inline constexpr bool __can_make_from_tuple_v< _Tp, _Tuple, index_sequence<_Idx...>> =
+    is_constructible_v<_Tp, decltype(std::get<_Idx>(std::declval<_Tuple>()))...>;
 
-template <class _Tp, class _Tuple,
-          class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
-inline constexpr bool __can_make_from_tuple = false;
+template <class _Tp, class _Tuple, class _Seq = make_index_sequence<tuple_size_v<__remove_cvref_t<_Tuple>>>>
+inline constexpr bool __can_nothrow_make_from_tuple_v = false;
 
 template <class _Tp, class _Tuple, size_t... _Idx>
-inline constexpr bool __can_make_from_tuple<_Tp, _Tuple, index_sequence<_Idx...>,
-    enable_if_t<is_constructible_v<_Tp, decltype(std::get<_Idx>(std::declval<_Tuple>()))...>>> = true;
-
-// Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9,
-// the standard allows to impose requirements, we constraint std::make_from_tuple to make std::make_from_tuple
-// SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of std::__make_from_tuple_impl
-// so that std::__make_from_tuple_impl will have the same advantages when used alone.
-#if _LIBCPP_STD_VER >= 20
-template <class _Tp, class _Tuple>
-  requires __can_make_from_tuple<_Tp, _Tuple> // strengthen
-#else
-template <class _Tp, class _Tuple, class = enable_if_t<__can_make_from_tuple<_Tp, _Tuple>>> // strengthen
-#endif // _LIBCPP_STD_VER >= 20
+inline constexpr bool __can_nothrow_make_from_tuple_v< _Tp, _Tuple, index_sequence<_Idx...>> =
+    is_nothrow_constructible_v<_Tp, decltype(std::get<_Idx>(std::declval<_Tuple>()))...>;
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp make_from_tuple(_Tuple&& __t)
-  noexcept(noexcept(std::__make_from_tuple_impl<_Tp>(std::forward<_Tuple>(__t),
-                    make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>()))) {
-#if _LIBCPP_STD_VER >= 23
+template <class _Tp, class _Tuple, enable_if_t<__can_make_from_tuple_v<_Tp, _Tuple>, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp
+make_from_tuple(_Tuple&& __tuple) noexcept(__can_nothrow_make_from_tuple_v<_Tp, _Tuple>) {
+#      if _LIBCPP_STD_VER >= 23
   if constexpr (tuple_size_v<remove_reference_t<_Tuple>> == 1) {
     static_assert(!std::reference_constructs_from_temporary_v<_Tp, decltype(std::get<0>(std::declval<_Tuple>()))>,
                   "Attempted construction of reference element binds to a temporary whose lifetime has ended");
   }
-#endif // _LIBCPP_STD_VER >= 23
-  return std::__make_from_tuple_impl<_Tp>(
-        std::forward<_Tuple>(__t), make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>());
+#      endif // _LIBCPP_STD_VER >= 23
+  return std::apply([]<class... _Args>(_Args&&... __args) { return _Tp(std::forward<_Args>(__args)...); },
+                    std::forward<_Tuple>(__tuple));
 }
 
-#  endif // _LIBCPP_STD_VER >= 17
+#    endif // _LIBCPP_STD_VER >= 17
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // !defined(_LIBCPP_CXX03_LANG)
+#  endif // !defined(_LIBCPP_CXX03_LANG)
 
 _LIBCPP_POP_MACROS
 
-// clang-format on
-
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_TUPLE
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp
index 8b9e94a1c1ad9..67281ea1864a5 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp
@@ -210,24 +210,6 @@ template <class T, class Tuple>
 static constexpr bool can_make_from_tuple =
     std::is_same_v<decltype(test_make_from_tuple<T, Tuple>(T{}, Tuple{})), std::uint8_t>;
 
-#ifdef _LIBCPP_VERSION
-template <class T, class Tuple>
-auto test_make_from_tuple_impl(T&&, Tuple&& t)
-    -> decltype(std::__make_from_tuple_impl<T>(
-                    t, typename std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>()),
-                std::uint8_t()) {
-  return 0;
-}
-template <class T, class Tuple>
-uint32_t test_make_from_tuple_impl(...) {
-  return 0;
-}
-
-template <class T, class Tuple>
-static constexpr bool can_make_from_tuple_impl =
-    std::is_same_v<decltype(test_make_from_tuple_impl<T, Tuple>(T{}, Tuple{})), std::uint8_t>;
-#endif // _LIBCPP_VERSION
-
 struct A {
   int a;
 };
@@ -263,27 +245,6 @@ static_assert(can_make_from_tuple<long, std::tuple<int>>);
 static_assert(can_make_from_tuple<double, std::tuple<float>>);
 static_assert(can_make_from_tuple<float, std::tuple<double>>);
 
-// Test std::__make_from_tuple_impl constraints.
-
-// reinterpret_cast
-LIBCPP_STATIC_ASSERT(!can_make_from_tuple_impl<int*, std::tuple<A*>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<A*, std::tuple<A*>>);
-
-// const_cast
-LIBCPP_STATIC_ASSERT(!can_make_from_tuple_impl<char*, std::tuple<const char*>>);
-LIBCPP_STATIC_ASSERT(!can_make_from_tuple_impl<volatile char*, std::tuple<const volatile char*>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<volatile char*, std::tuple<volatile char*>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<char*, std::tuple<char*>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<const char*, std::tuple<char*>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<const volatile char*, std::tuple<volatile char*>>);
-
-// static_cast
-LIBCPP_STATIC_ASSERT(!can_make_from_tuple_impl<int, std::tuple<D>>);
-LIBCPP_STATIC_ASSERT(!can_make_from_tuple_impl<D, std::tuple<int>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<long, std::tuple<int>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<double, std::tuple<float>>);
-LIBCPP_STATIC_ASSERT(can_make_from_tuple_impl<float, std::tuple<double>>);
-
 } // namespace LWG3528
 
 static_assert(LWG3528::can_make_from_tuple<int, std::tuple<>>);



More information about the libcxx-commits mailing list