[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