On Mon, Jun 9, 2025 at 4:07 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> From: Yihan Wang <yronglin...@gmail.com> > > Implement LWG3528 to make std::make_from_tuple SFINAE friendly. > > libstdc++-v3/ChangeLog: > > * include/std/tuple (__can_make_from_tuple): New variable > template. > (__make_from_tuple_impl): Add static_assert. > (make_from_tuple): Constrain using __can_make_from_tuple. > * testsuite/20_util/tuple/dr3528.cc: New test. > > Signed-off-by: Yihan Wang <yronglin...@gmail.com> > Co-authored-by: Jonathan Wakely <jwak...@redhat.com> > --- > > Here's a simpler version of Yihan's patch, with constraints on > std::make_from_tuple but not on __make_from_tuple_impl, and with an > original test rather than one copied from MSVC and libc++. > > Lightly tested on x86_64-linux, full testing underway. > LGTM. Thanks. > > libstdc++-v3/include/std/tuple | 24 +++++++++- > .../testsuite/20_util/tuple/dr3528.cc | 44 +++++++++++++++++++ > 2 files changed, 66 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/20_util/tuple/dr3528.cc > > diff --git a/libstdc++-v3/include/std/tuple > b/libstdc++-v3/include/std/tuple > index 2e69af13a98b..b39ce710984c 100644 > --- a/libstdc++-v3/include/std/tuple > +++ b/libstdc++-v3/include/std/tuple > @@ -2939,19 +2939,39 @@ _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; > + > + // _GLIBCXX_RESOLVE_LIB_DEFECTS > + // 3528. make_from_tuple can perform (the equivalent of) a C-style cast > + 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>>; > #if __has_builtin(__reference_constructs_from_temporary) > diff --git a/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc > b/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc > new file mode 100644 > index 000000000000..a96c6b75e209 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc > @@ -0,0 +1,44 @@ > +// { dg-do compile { target c++17 } } > + > +// LWG 3528. make_from_tuple can perform (the equivalent of) a C-style > cast > + > +#include <tuple> > +#include <array> > +#include <utility> > + > +template<typename T, typename Tuple> > +using make_t = decltype(std::make_from_tuple<T>(std::declval<Tuple>())); > + > +template<typename T, typename Tuple, typename = void> > +constexpr bool can_make = false; > +template<typename T, typename Tuple> > +constexpr bool can_make<T, Tuple, std::void_t<make_t<T, Tuple>>> = true; > + > +static_assert( can_make<int, std::tuple<int>> ); > +static_assert( can_make<int, std::tuple<int>&> ); > +static_assert( can_make<int, const std::tuple<int>&> ); > +static_assert( can_make<int, std::array<short, 1>> ); > +static_assert( can_make<int, const std::array<short, 1>&&> ); > +static_assert( can_make<std::tuple<int, int>, std::pair<unsigned, long>> > ); > +static_assert( can_make<const int*, std::tuple<int*>> ); > +static_assert( can_make<void*, std::tuple<int*>> ); > +static_assert( can_make<int, std::tuple<>> ); > +static_assert( ! can_make<int, std::tuple<int, int>> ); > +static_assert( ! can_make<int, std::pair<short, char>> ); > +static_assert( ! can_make<int, std::pair<short, char>&> ); > +static_assert( ! can_make<int, std::tuple<const char*>> ); > +static_assert( ! can_make<int*, std::tuple<const int*>> ); > +static_assert( ! can_make<int*, std::tuple<void*>> ); > +static_assert( ! can_make<int, std::array<int, 2>> ); > +static_assert( ! can_make<void, std::tuple<>> ); > +static_assert( ! can_make<void, std::array<int, 1>> ); > + > +struct Two > +{ > + Two(const char*, int); > +}; > + > +static_assert( can_make<Two, std::tuple<char*, unsigned>> ); > +static_assert( ! can_make<Two, std::tuple<const char*, int, int>> ); > +static_assert( can_make<Two, std::pair<const char*, long>> ); > +static_assert( ! can_make<Two, std::pair<int*, long>> ); > -- > 2.49.0 > >