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).

Reply via email to