On 08/06/25 01:32 +0800, Yrong wrote:
Hi libstdc++ experts,
This patch implement LWG3528 and also implement an improvement that makes
std::make_from_tuple SFINAE friendly.
I have implemented this LWG issue and SFINAE enhancements for libc++ and
Microsoft STL.
This is my first patch for libstdc++, hope I didn't missing anything.
Please feel free to tell me if I messed anything up!
Thanks for the contribution!
The patch is attached as mimetype application/octet-stream which makes
it harder to read and reply to in most mailers (it must be saved to
file and then opened separately, rather than just replying inline). I
think it you save it as a .txt file and attach it then gmail will make
it easier to work with. (Even better is to use 'git send-email' but
that requires some configuration to be able to send mail via gmail
servers, see https://git-send-email.io/ for step-by-step intructions).
Please confirm that you are using Signed-off-by: with the meaning
explained at https://gcc.gnu.org/dco.html
From 7dfeb6bdbae91628244b0712dd46c5bf38bb0fff Mon Sep 17 00:00:00 2001
From: Yihan Wang <yronglin...@gmail.com>
Date: Sun, 8 Jun 2025 01:06:26 +0800
Subject: [PATCH] libstdc++: Implement LWG3528 make_from_tuple can perform (the
equivalent of) a C-style cast
Implement LWG3528 and make std::make_from_tuple SFINAE friendly.
libstdc++-v3/ChangeLog:
* include/std/tuple:
* testsuite/20_util/tuple/make_from_tuple/dr3528.cc: New test.
Signed-off-by: Yihan Wang <yronglin...@gmail.com>
---
libstdc++-v3/include/std/tuple | 37 ++++-
.../20_util/tuple/make_from_tuple/dr3528.cc | 144 ++++++++++++++++++
2 files changed, 175 insertions(+), 6 deletions(-)
create mode 100644
libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/dr3528.cc
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 2e69af13a98..6f5d2438eaf 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -2939,15 +2939,40 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
#ifdef __cpp_lib_make_from_tuple // C++ >= 17
- template <typename _Tp, typename _Tuple, size_t... _Idx>
+#ifdef __cpp_concepts // >= C++20
+ template <typename _Tp, typename _Tuple, size_t... _Idx>
+ requires is_constructible_v<
+ _Tp, decltype(std::get<_Idx>(std::declval<_Tuple>()))...>
constexpr _Tp
- __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>)
+#else
+ template <typename _Tp, typename _Tuple, size_t... _Idx>
+ constexpr __enable_if_t<
+ is_constructible_v<_Tp,
+
decltype(std::get<_Idx>(std::declval<_Tuple>()))...>,
+ _Tp>
+#endif
Do we really need to constrain __make_from_tuple_impl when it's only
called by std::make_from_tuple, which is already constrained?
We could just put a static_assert in the body of
__make_from_tuple_impl to ensure that it isn't misused.
+ __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>)
This line doesn't need to be reindented, it was correct before.
{ return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); }
+ template <typename _Tp, typename _Tuple,
+ typename _Seq =
+
make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>>
+ constexpr inline bool __can_make_from_tuple = false;
+
+ template <typename _Tp, typename _Tuple, size_t... _Idx>
+ constexpr inline bool __can_make_from_tuple<_Tp, _Tuple,
+ index_sequence<_Idx...>> =
+ is_constructible_v<_Tp,
+
decltype(std::get<_Idx>(std::declval<_Tuple>()))...>;
+
#if __cpp_lib_tuple_like // >= C++23
- template <typename _Tp, __tuple_like _Tuple>
+ template <typename _Tp, __tuple_like _Tuple>
This should not be reindented.
#else
- template <typename _Tp, typename _Tuple>
+ template <typename _Tp, typename _Tuple,
+ typename = __enable_if_t<__can_make_from_tuple<_Tp, _Tuple>>>
+#endif
+#ifdef __cpp_concepts // >= C++20
+ requires __can_make_from_tuple<_Tp, _Tuple>
#endif
Doing it like this means that for C++20 we use enable_if *and* a
requires-clause, but we don't need both.
constexpr _Tp
make_from_tuple(_Tuple&& __t)
@@ -2962,7 +2987,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif
return __make_from_tuple_impl<_Tp>(std::forward<_Tuple>(__t),
- make_index_sequence<__n>{});
+ make_index_sequence<__n>{});
}
#endif
@@ -2970,7 +2995,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<__tuple_like _TTuple, __tuple_like _UTuple,
template<typename> class _TQual, template<typename> class _UQual,
typename = make_index_sequence<tuple_size_v<_TTuple>>>
- struct __tuple_like_common_reference;
+ struct __tuple_like_common_reference;
This change *is* correct, but isn't related to make_from_tuple so
doesn't belong in this patch.
I think the diff below is a simpler implementation of the change,
using the new variable template as a "Mandates:" for
__make_from_tuple_impl and as a "Constraints:" for make_from_tuple.
We can do it with a single new #if / #else addition by giving
std::make_from_tuple a trailing-return-type and putting the
constraints there.
What do you think?
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -2939,19 +2939,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
#ifdef __cpp_lib_make_from_tuple // C++ >= 17
+ template <typename _Tp, typename _Tuple, typename _Seq
+ = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>>
+ constexpr bool __can_make_from_tuple = false;
+
+ template <typename _Tp, typename _Tuple, size_t... _Idx>
+ constexpr bool __can_make_from_tuple<_Tp, _Tuple, index_sequence<_Idx...>>
+ = is_constructible_v<_Tp,
+ decltype(std::get<_Idx>(std::declval<_Tuple>()))...>;
+
template <typename _Tp, typename _Tuple, size_t... _Idx>
constexpr _Tp
__make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>)
- { return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); }
+ {
+ static_assert(__can_make_from_tuple<_Tp, _Tuple,
index_sequence<_Idx...>>);
+ return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...);
+ }
#if __cpp_lib_tuple_like // >= C++23
template <typename _Tp, __tuple_like _Tuple>
#else
template <typename _Tp, typename _Tuple>
#endif
- constexpr _Tp
+ constexpr auto
make_from_tuple(_Tuple&& __t)
noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>)
+#ifdef __cpp_concepts // >= C++20
+ -> _Tp
+ requires __can_make_from_tuple<_Tp, _Tuple>
+#else
+ -> __enable_if_t<__can_make_from_tuple<_Tp, _Tuple>, _Tp>
+#endif
{
constexpr size_t __n = tuple_size_v<remove_reference_t<_Tuple>>;
(I initially thought we could re-use __unpack_std_tuple for the new
constraints, but that only works for std::tuple not arbitrary
tuple-like types. We need to fix __unpack_std_tuple anyway because
P2517R1 requires std::apply to have a correct noexcept-specifier for
arbitrary tuple-like types, and we need to consider whether std::get
can throw, which the current noexcept-specifier doesn't do. That is a
separate topic though, not part of this patch.)
diff --git a/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/dr3528.cc
b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/dr3528.cc
new file mode 100644
index 00000000000..9445151a2b9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/dr3528.cc
@@ -0,0 +1,144 @@
+// { dg-do compile { target c++17 } }
+// { dg-do compile { target c++20 } }
Only the first line is needed, c++17 means "C++17 and later".
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//===----------------------------------------------------------------------===//
+
+// derived from libc++'s test files:
+// * std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp
Can we avoid copying the tests? I'll write some tests for this
(without reading the rest of your patch).