From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id C74913858420 for ; Wed, 24 Jan 2024 19:47:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C74913858420 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C74913858420 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706125651; cv=none; b=kaIUiNizLLCeVkXM13/e3s1Im8yVTMEn6GCMxpvb9sq4SUG1DQYu8bj/o2E6cYw9GitYvKqH0gC2DrjnJ+ulqoYkKO4o/UNoWhB3uDM9zITKMV2129clHuK+JPX+qIG8D6zKll65jt5icBH/cPMqFRfq6H8OPoPiyR2S/LtjrkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706125651; c=relaxed/simple; bh=LjtHrSPo+D0+R2IuxnYaqcnG6TpRr8cECWJLmOFVAo4=; h=DKIM-Signature:From:Date:To:Subject:Message-ID:MIME-Version; b=hhbAfyNzRL0DyUNjG/Dnn6og+2nzrvHz9UIpjuOUjQD3/V73dUSTaHl1TF5gTlPPepfOH5ZXxNlyDGTesOjQU7yvpUfwDMol0ONUDRlUNqgHdtkUMWS/ghaD2xts0edJspOLZsSJH3PUMbghOlGeQH3TAHi3IlpyLw1XMLBEPwY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1706125645; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=MYzUs/P39o77k26HrPFxzVgE29Vcz5uWMGowwaV4TNo=; b=LpPy+kEptru1KyhsL6gRYoJ8vbx7h8PthmK/uOTBgQKeSWBwkZ2JQPwt/JLNSrOjOTo+US jv+DH6a9rP990vl2y16yEY2y8TkaNBuwGzetlbW3SCgQQVdyoxOSUAHE8hgjd7ZAd43ugd wtmpe9G1Z+2B6l2j7Ejn3MNBQFTSlQ8= Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-82-zaZ8793RNa-x0P6k5mQq8g-1; Wed, 24 Jan 2024 14:47:24 -0500 X-MC-Unique: zaZ8793RNa-x0P6k5mQq8g-1 Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-783bc28ebdaso11933185a.2 for ; Wed, 24 Jan 2024 11:47:23 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706125643; x=1706730443; h=mime-version:references:message-id:in-reply-to:subject:cc:to:date :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MYzUs/P39o77k26HrPFxzVgE29Vcz5uWMGowwaV4TNo=; b=VnCz53M2UkpI5cPUgUmP+7L98VktFkir41Yma9Jz+gg1e2m/YBf4Cy2N0ywi/gyhsL lVsxXNN9qvg5QA/rKlVD7TruyD9RiVWS1hXMYXGXqjrdM7XY1EN/4gGodh3EyoC7cnjy CMm3RdosnaRimzpA1T2WCejMmx9MVa3dMq4z+BSiIz1a2R16WxjpjRVGQJt7V6vK23Kg T/N9VmFH91YKs9j5DGxyWDQYkftqlhTULUvP179TVr0B+B5JMiaBpLTs2OVjummaTPZA Uk9/QeITQlW089d4In+7M2XIzC47Um1+FiiFd48CeP23BKMiFfQ8ka/6dYGIeeFw6ueJ UFmw== X-Gm-Message-State: AOJu0Yze5ii/nG6GmjEbML1Gu9Jv9F1B/utGErCDjo+ChRIJh9CVJBaT 7O3le41pfH0ElO/ZcvHyqdybgXHUqS87vJugNcnJ2yiztTVck8jWkXjpVlxSFu56zhbkXDmOaUI W1qnniYSu4tA1yvUPuic6aJafcY8dLI4ltJIEJtnmZFnU3NF2J7BWNmFFYV6OkHU= X-Received: by 2002:a05:620a:51:b0:783:2320:9704 with SMTP id t17-20020a05620a005100b0078323209704mr9105039qkt.26.1706125642306; Wed, 24 Jan 2024 11:47:22 -0800 (PST) X-Google-Smtp-Source: AGHT+IEXa5Bv3fRFFAYZr3hnuW50ljXlQfVrMmW2ChrXqWWpJChF4otYWKI4brLFufNEFFv6i1D4/g== X-Received: by 2002:a05:620a:51:b0:783:2320:9704 with SMTP id t17-20020a05620a005100b0078323209704mr9105020qkt.26.1706125641631; Wed, 24 Jan 2024 11:47:21 -0800 (PST) Received: from [192.168.1.130] (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id i11-20020a05620a248b00b007839388e503sm4141658qkn.81.2024.01.24.11.47.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jan 2024 11:47:21 -0800 (PST) From: Patrick Palka X-Google-Original-From: Patrick Palka Date: Wed, 24 Jan 2024 14:47:20 -0500 (EST) To: Patrick Palka cc: Jonathan Wakely , gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: Re: [PATCH 2/2] libstdc++: Implement P2165R4 changes to std::pair/tuple/etc In-Reply-To: <817fa7d4-7a12-c242-7405-fb53ee8e5e74@idea> Message-ID: <4bdfa6ed-a975-3748-be92-aa496211cf85@idea> References: <20240123235303.1540890-1-ppalka@redhat.com> <20240123235303.1540890-2-ppalka@redhat.com> <817fa7d4-7a12-c242-7405-fb53ee8e5e74@idea> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII X-Spam-Status: No, score=-14.2 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,RCVD_IN_SORBS_WEB,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Wed, 24 Jan 2024, Patrick Palka wrote: > On Wed, 24 Jan 2024, Jonathan Wakely wrote: > > > On Wed, 24 Jan 2024 at 15:24, Patrick Palka wrote: > > > > > > On Wed, 24 Jan 2024, Jonathan Wakely wrote: > > > > > > > On Tue, 23 Jan 2024 at 23:54, Patrick Palka wrote: > > > > > diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h > > > > > index b81b479ad43..a9b20fbe7ca 100644 > > > > > --- a/libstdc++-v3/include/bits/stl_pair.h > > > > > +++ b/libstdc++-v3/include/bits/stl_pair.h > > > > > @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > /// @cond undocumented > > > > > > > > > > // Forward declarations. > > > > > + template > > > > > + struct pair; > > > > > > > > We have a compiler bug where a forward declaration without template > > > > parameter names causes bad diagnostics later. The compiler seems to > > > > try to use the parameter names from the first decl it sees, so we end > > > > up with things like even when there's a name > > > > available at the site of the actual error. So I think we should name > > > > these _T1 and _T2 here. > > > > > > Will fix. > > > > > > > > > > > > + > > > > > template > > > > > class tuple; > > > > > > > > > > + // Declarations of std::array and its std::get overloads, so that > > > > > + // std::tuple_cat can use them if is included before . > > > > > + // We also declare the other std::get overloads here so that they're > > > > > + // visible to the P2165R4 tuple-like constructors of pair and tuple. > > > > > + template > > > > > + struct array; > > > > > + > > > > > template > > > > > struct _Index_tuple; > > > > > > > > > > + template > > > > > + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& > > > > > + get(pair<_Tp1, _Tp2>& __in) noexcept; > > > > > + > > > > > + template > > > > > + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& > > > > > + get(pair<_Tp1, _Tp2>&& __in) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& > > > > > + get(const pair<_Tp1, _Tp2>& __in) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& > > > > > + get(const pair<_Tp1, _Tp2>&& __in) noexcept; > > > > > + > > > > > + template > > > > > + constexpr __tuple_element_t<__i, tuple<_Elements...>>& > > > > > + get(tuple<_Elements...>& __t) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>& > > > > > + get(const tuple<_Elements...>& __t) noexcept; > > > > > + > > > > > + template > > > > > + constexpr __tuple_element_t<__i, tuple<_Elements...>>&& > > > > > + get(tuple<_Elements...>&& __t) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>&& > > > > > + get(const tuple<_Elements...>&& __t) noexcept; > > > > > + > > > > > + template > > > > > + constexpr _Tp& > > > > > + get(array<_Tp, _Nm>&) noexcept; > > > > > + > > > > > + template > > > > > + constexpr _Tp&& > > > > > + get(array<_Tp, _Nm>&&) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const _Tp& > > > > > + get(const array<_Tp, _Nm>&) noexcept; > > > > > + > > > > > + template > > > > > + constexpr const _Tp&& > > > > > + get(const array<_Tp, _Nm>&&) noexcept; > > > > > + > > > > > #if ! __cpp_lib_concepts > > > > > // Concept utility functions, reused in conditionally-explicit > > > > > // constructors. > > > > > @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > #endif // lib concepts > > > > > #endif // C++11 > > > > > > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > + template > > > > > + inline constexpr bool __is_tuple_v = false; > > > > > + > > > > > + template > > > > > + inline constexpr bool __is_tuple_v> = true; > > > > > + > > > > > + // TODO: Reuse __is_tuple_like from ? > > > > > + template > > > > > + inline constexpr bool __is_tuple_like_v = false; > > > > > + > > > > > + template > > > > > + inline constexpr bool __is_tuple_like_v> = true; > > > > > + > > > > > + template > > > > > + inline constexpr bool __is_tuple_like_v> = true; > > > > > + > > > > > + template > > > > > + inline constexpr bool __is_tuple_like_v> = true; > > > > > + > > > > > + // __is_tuple_like_v is defined in . > > > > > + > > > > > + template > > > > > + concept __tuple_like = __is_tuple_like_v>; > > > > > + > > > > > + template > > > > > + concept __pair_like = __tuple_like<_Tp> && tuple_size_v> == 2; > > > > > + > > > > > + template > > > > > + concept __eligible_tuple_like > > > > > + = __detail::__different_from<_Tp, _Tuple> && __tuple_like<_Tp> > > > > > + && (tuple_size_v> == tuple_size_v<_Tuple>) > > > > > + && !ranges::__detail::__is_subrange>; > > > > > + > > > > > + template > > > > > + concept __eligible_pair_like > > > > > + = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp> > > > > > + && !ranges::__detail::__is_subrange>; > > > > > +#endif // C++23 > > > > > + > > > > > template class __pair_base > > > > > { > > > > > #if __cplusplus >= 201103L && ! __cpp_lib_concepts > > > > > @@ -295,6 +393,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > return false; > > > > > #endif > > > > > } > > > > > + > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > + template > > > > > + static constexpr bool > > > > > + _S_constructible_from_pair_like() > > > > > + { > > > > > + return _S_constructible(std::declval<_UPair>())), > > > > > + decltype(std::get<1>(std::declval<_UPair>()))>(); > > > > > + } > > > > > + > > > > > + template > > > > > + static constexpr bool > > > > > + _S_convertible_from_pair_like() > > > > > + { > > > > > + return _S_convertible(std::declval<_UPair>())), > > > > > + decltype(std::get<1>(std::declval<_UPair>()))>(); > > > > > + } > > > > > +#endif // C++23 > > > > > /// @endcond > > > > > > > > > > public: > > > > > @@ -393,6 +509,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > pair(const pair<_U1, _U2>&&) = delete; > > > > > #endif // C++23 > > > > > > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > + template<__eligible_pair_like _UPair> > > > > > + requires (_S_constructible_from_pair_like<_UPair>()) > > > > > + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) > > > > > + pair(_UPair&& __p) > > > > > + : first(std::get<0>(std::forward<_UPair>(__p))), > > > > > + second(std::get<1>(std::forward<_UPair>(__p))) > > > > > + { } > > > > > +#endif // C++23 > > > > > > > > I think this needs to be constrained with !_S_dangles<...>() and we > > > > need a deleted overload with the same constraints, except for > > > > _S_dangles being true. > > > > > > > > And that should be covered by a test. > > > > > > Oops, will fix. Should the deleted overloads carry over the > > > conditionally explicit specifier? I noticed pair's deleted overloads > > > do, but tuple's overloads don't. > > > > Huh, oops. Does an explicit ctor participate in overload resolution > > and then get checked if it's usable, or is it remove from overload > > resolution earlier? > > It looks like I decided the answer was the latter for pair and the > > former for tuple. > > AFAICT if we know the explicitness of the ctor early (either because the > ctor is a non-template or it has a non-dependent explicit-spec), then we > remove it from the overload set early, in which case it'd be useful to > give the deleted ctor the right explicit-spec to avoid unecessary > constraint checking etc. > > Otherwise, for ctor templates with a dependent explicit-spec such as > these tuple/pair ones, we must wait until after deduction to check > explicitness which means constraints are checked first. And by then > we already know that __dangles is true, so we presumably want overload > resolution to fail regardless. Whether that's due to selecting a > (viable) deleted non-explicit ctor (if we omit the explicit-spec) or due > to there being no viable non-explicit ctor (if we carry over the > explicit-spec) shouldn't make a difference, I think? > > So it seems unnecessary to give these deleted overloads an > explicit-spec; it wouldn't be considered unless overload resolution > is destined to fail anyway. > > I'm not totally confident about this assessment though so I'll > just carry over the explicit-spec for now. I ended up hedging my bets and including the explicit-spec in the deleted ctors of pair and omitting it in those of tuple, so that we continue to be locally consistent. > > > > > > > > > > > > > > > > > > > > > > > diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple > > > > > index be92f1eb973..182f3cc5e6a 100644 > > > > > --- a/libstdc++-v3/include/std/tuple > > > > > +++ b/libstdc++-v3/include/std/tuple > > > > > @@ -50,6 +50,7 @@ > > > > > #define __glibcxx_want_apply > > > > > #define __glibcxx_want_make_from_tuple > > > > > #define __glibcxx_want_ranges_zip > > > > > +#define __glibcxx_want_tuple_like > > > > > #include > > > > > > > > > > namespace std _GLIBCXX_VISIBILITY(default) > > > > > @@ -246,6 +247,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > _Head _M_head_impl; > > > > > }; > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; }; > > > > > + > > > > > + // Forward declared for use by the operator<=> overload for tuple-like types. > > > > > + template > > > > > + constexpr _Cat > > > > > + __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); > > > > > + > > > > > + template > > > > + size_t _Idx0, size_t... _Idxs> > > > > > + constexpr _Cat > > > > > + __tuple_cmp(const _Tp& __t, const _Up& __u, > > > > > + index_sequence<_Idx0, _Idxs...>); > > > > > +#endif // C++23 > > > > > + > > > > > /** > > > > > * Contains the actual implementation of the @c tuple template, stored > > > > > * as a recursive inheritance hierarchy from the first element (most > > > > > @@ -342,6 +358,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > { } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr > > > > > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<_Is...>) > > > > > + : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...) > > > > > + { } > > > > > +#endif // C++23 > > > > > + > > > > > template > > > > > _GLIBCXX20_CONSTEXPR > > > > > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > > > > > @@ -428,6 +452,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > { } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr > > > > > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& __a, > > > > > + _UTuple&& __u, index_sequence<_Is...>) > > > > > + : _Tuple_impl(__tag, __a, std::get<_Is>(std::forward<_UTuple>(__u))...) > > > > > + { } > > > > > +#endif // C++23 > > > > > + > > > > > template > > > > > _GLIBCXX20_CONSTEXPR > > > > > void > > > > > @@ -470,6 +503,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr void > > > > > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) > > > > > + { > > > > > + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); > > > > > + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); > > > > > + } > > > > > + > > > > > + template > > > > > + constexpr void > > > > > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const > > > > > + { > > > > > + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); > > > > > + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); > > > > > + } > > > > > +#endif // C++23 > > > > > + > > > > > protected: > > > > > _GLIBCXX20_CONSTEXPR > > > > > void > > > > > @@ -563,6 +614,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > { } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr > > > > > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<0>) > > > > > + : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u))) > > > > > + { } > > > > > +#endif // C++23 > > > > > + > > > > > template > > > > > _GLIBCXX20_CONSTEXPR > > > > > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > > > > > @@ -633,6 +692,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > { } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr > > > > > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& __a, > > > > > + _UTuple&& __u, index_sequence<0>) > > > > > + : _Tuple_impl(__tag, __a, std::get<0>(std::forward<_UTuple>(__u))) > > > > > + { } > > > > > +#endif // C++23 > > > > > + > > > > > template > > > > > _GLIBCXX20_CONSTEXPR > > > > > void > > > > > @@ -667,6 +735,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > } > > > > > #endif // C++23 > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + constexpr void > > > > > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) > > > > > + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } > > > > > + > > > > > + template > > > > > + constexpr void > > > > > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) const > > > > > + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } > > > > > +#endif // C++23 > > > > > + > > > > > protected: > > > > > _GLIBCXX20_CONSTEXPR > > > > > void > > > > > @@ -846,6 +926,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > #endif > > > > > } > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template > > > > > + static consteval bool > > > > > + __constructible_from_tuple_like() > > > > > + { > > > > > + return [](index_sequence<_Is...>) { > > > > > + return __constructible(std::declval<_UTuple>()))...>(); > > > > > + }(make_index_sequence{}); > > > > > + } > > > > > + > > > > > + template > > > > > + static consteval bool > > > > > + __convertible_from_tuple_like() > > > > > + { > > > > > + return [](index_sequence<_Is...>) { > > > > > + return __convertible(std::declval<_UTuple>()))...>(); > > > > > + }(make_index_sequence{}); > > > > > > > > These new functions can use index_sequence_for<_Elements...>{} here, > > > > so you don't need the sizeof.... > > > > That applies several times below as well. > > > > > > > > I think it's semantically identical, just a little shorter. I don't > > > > know if there's any compilation speed benefit either way. Maybe > > > > sizeof...(_Elements) is cheaper than expanding the pack into the > > > > index_sequence_for alias template? It probably a little cheaper to use make_index_sequence directly, but I just didn't know about index_sequence_for :) Consider that changed. > > > > > > > > > > > > > + } > > > > > +#endif // C++23 > > > > > + > > > > > public: > > > > > constexpr > > > > > explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...)) > > > > > @@ -1016,10 +1116,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > tuple(const pair<_U1, _U2>&&) = delete; > > > > > #endif // C++23 > > > > > > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > > > > > - template<__tuple_like _UTuple> > > > > > - constexpr explicit(...) > > > > > - tuple(_UTuple&& __u); > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template<__eligible_tuple_like _UTuple> > > > > > + requires (__constructible_from_tuple_like<_UTuple>()) > > > > > + && (!__use_other_ctor<_UTuple>()) > > > > > + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) > > > > > + tuple(_UTuple&& __u) > > > > > + : _Inherited(__tuple_like_tag_t{}, > > > > > + std::forward<_UTuple>(__u), > > > > > + make_index_sequence{}) > > > > > + { } > > > > > #endif // C++23 > > > > > > > > > > // Allocator-extended constructors. > > > > > @@ -1202,10 +1308,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete; > > > > > #endif // C++23 > > > > > > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > > > > > - template > > > > > - constexpr explicit(...) > > > > > - tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u); > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > + template _UTuple> > > > > > + requires (__constructible_from_tuple_like<_UTuple>()) > > > > > + && (!__use_other_ctor<_UTuple>()) > > > > > + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) > > > > > + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u) > > > > > + : _Inherited(__tuple_like_tag_t{}, > > > > > + __tag, __a, std::forward<_UTuple>(__u), > > > > > + make_index_sequence{}) > > > > > + { } > > > > > #endif // C++23 > > > > > > > > For some reason these two new constructors aren't deleted if they > > > > create dangling refs. I don't know why. > > > > > > Hmm, seems like an oversight. Shall we proactively implement them? > > > > Yes, I think so. I can't see why we would want to permit a dangling > > reference there. > > > > e.g. > > std::array a{}; > > std::tuple t(a); > > Sounds good. In v2: * Named the template parameters of the forward declaration of pair. * Added dangling checks for the new tuple and pair constructors and corresponding tests. * Replaced make_index_sequence with index_sequence_for where applicable. -- >8 -- Subject: [PATCH 2/2] libstdc++: Implement P2165R4 changes to std::pair/tuple/etc libstdc++-v3/ChangeLog: * include/bits/ranges_util.h (__detail::__pair_like): Don't define in C++23 mode. (__detail::__pair_like_convertible_from): Adjust as per P2165R4. (__detail::__is_subrange): Moved from . (__detail::__is_tuple_like_v): Likewise. * include/bits/stl_iterator.h: Include for C++23. (__different_from): Move to . (__iter_key_t): Adjust for C++23 as per P2165R4. (__iter_val_t): Likewise. * include/bits/stl_pair.h (pair, array): Forward declare. (get): Forward declare all overloads relevant to P2165R4 tuple-like constructors. (__is_tuple_v): Define for C++23. (__is_tuple_like_v): Define for C++23. (__tuple_like): Define for C++23 as per P2165R4. (__pair_like): Define for C++23 as per P2165R4. (__eligibile_tuple_like): Define for C++23. (__eligibile_pair_like): Define for C++23. (pair::_S_constructible_from_pair_like): Define for C++23. (pair::_S_convertible_from_pair_like): Define for C++23. (pair::_S_dangles_from_pair_like): Define for C++23. (pair::pair): Define overloads taking a tuple-like type for C++23 as per P2165R4. (pair::_S_assignable_from_tuple_like): Define for C++23. (pair::_S_const_assignable_from_tuple_like): Define for C++23. (pair::operator=): Define overloads taking a tuple-like type for C++23 as per P2165R4. * include/bits/utility.h (ranges::__detail::__is_subrange): Moved from . * include/bits/version.def (tuple_like): Define for C++23. * include/bits/version.h: Regenerate. * include/std/concepts (__different_from): Moved from . (ranges::__swap::__adl_swap): Clarify which __detail namespace. * include/std/map (__cpp_lib_tuple_like): Define C++23. * include/std/ranges (__detail::__is_subrange): Moved to . (__detail::__is_subrange): Moved to (__detail::__has_tuple_element): Adjust for C++23 as per P2165R4. (__detail::__tuple_or_pair): Remove as per P2165R4. Replace all uses with plain tuple as per P2165R4. * include/std/tuple (__cpp_lib_tuple_like): Define for C++23. (__tuple_like_tag_t): Define for C++23. (__tuple_cmp): Forward declare for C++23. (_Tuple_impl::_Tuple_impl): Define overloads taking __tuple_like_tag_t and a tuple-like type for C++23. (_Tuple_impl::_M_assign): Likewise. (tuple::__constructible_from_tuple_like): Define for C++23. (tuple::__convertible_from_tuple_like): Define for C++23. (tuple::__dangles_from_tuple_like): Define for C++23. (tuple::tuple): Define overloads taking a tuple-like type for C++23 as per P2165R4. (tuple::__assignable_from_tuple_like): Define for C++23. (tuple::__const_assignable_from_tuple_like): Define for C++23. (tuple::operator=): Define overloads taking a tuple-like type for C++23 as per P2165R4. (tuple::__tuple_like_common_comparison_category): Define for C++23. (tuple::operator<=>): Define overload taking a tuple-like type for C++23 as per P2165R4. (array, get): Forward declarations moved to . (tuple_cat): Constrain with __tuple_like for C++23 as per P2165R4. (apply): Likewise. (make_from_tuple): Likewise. (__tuple_like_common_reference): Define for C++23. (basic_common_reference): Adjust as per P2165R4. (__tuple_like_common_type): Define for C++23. (common_type): Adjust as per P2165R4. * include/std/unordered_map (__cpp_lib_tuple_like): Define for C++23. * include/std/utility (__cpp_lib_tuple_like): Define for C++23. * testsuite/std/ranges/zip/1.cc (test01): Adjust to handle pair and 2-tuple interchangeably. * testsuite/20_util/pair/p2165r4.cc: New test. * testsuite/20_util/tuple/p2165r4.cc: New test. --- libstdc++-v3/include/bits/ranges_util.h | 17 +- libstdc++-v3/include/bits/stl_iterator.h | 16 +- libstdc++-v3/include/bits/stl_pair.h | 182 ++++++++++ libstdc++-v3/include/bits/utility.h | 8 + libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 11 + libstdc++-v3/include/std/concepts | 11 +- libstdc++-v3/include/std/map | 1 + libstdc++-v3/include/std/ranges | 48 +-- libstdc++-v3/include/std/tuple | 323 ++++++++++++++--- libstdc++-v3/include/std/unordered_map | 1 + libstdc++-v3/include/std/utility | 1 + .../testsuite/20_util/pair/p2165r4.cc | 173 +++++++++ .../testsuite/20_util/tuple/p2165r4.cc | 335 ++++++++++++++++++ libstdc++-v3/testsuite/std/ranges/zip/1.cc | 4 +- 15 files changed, 1056 insertions(+), 83 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2165r4.cc create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index bb04c49f044..9b79c3a229d 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -224,6 +224,10 @@ namespace ranges && !__uses_nonqualification_pointer_conversion, decay_t<_To>>; +#if __glibcxx_tuple_like // >= C++23 + // P2165R4 version of __pair_like is defined in . +#else + // C++20 version of __pair_like from P2321R2. template concept __pair_like = !is_reference_v<_Tp> && requires(_Tp __t) @@ -235,10 +239,11 @@ namespace ranges { get<0>(__t) } -> convertible_to&>; { get<1>(__t) } -> convertible_to&>; }; +#endif template concept __pair_like_convertible_from - = !range<_Tp> && __pair_like<_Tp> + = !range<_Tp> && !is_reference_v<_Vp> && __pair_like<_Tp> && constructible_from<_Tp, _Up, _Vp> && __convertible_to_non_slicing<_Up, tuple_element_t<0, _Tp>> && convertible_to<_Vp, tuple_element_t<1, _Tp>>; @@ -463,8 +468,18 @@ namespace ranges using borrowed_subrange_t = __conditional_t, subrange>, dangling>; + + // __is_subrange is defined in . + template + inline constexpr bool __detail::__is_subrange> = true; } // namespace ranges +#if __glibcxx_tuple_like // >= C++23 + // __is_tuple_like_v is defined in . + template + inline constexpr bool __is_tuple_like_v> = true; +#endif + // The following ranges algorithms are used by , and are defined here // so that can avoid including all of . namespace ranges diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index d71a793e10d..560a10a7abe 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -78,6 +78,10 @@ # include #endif +#if __glibcxx_tuple_like // >= C++23 +# include // for tuple_element_t +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -95,10 +99,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template using __clamp_iter_cat = __conditional_t, _Limit, _Otherwise>; - - template - concept __different_from - = !same_as, remove_cvref_t<_Up>>; } #endif @@ -2983,11 +2983,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // of associative containers. template using __iter_key_t = remove_const_t< +#if __glibcxx_tuple_like // >= C++23 + tuple_element_t<0, typename iterator_traits<_InputIterator>::value_type>>; +#else typename iterator_traits<_InputIterator>::value_type::first_type>; +#endif template using __iter_val_t +#if __glibcxx_tuple_like // >= C++23 + = tuple_element_t<1, typename iterator_traits<_InputIterator>::value_type>; +#else = typename iterator_traits<_InputIterator>::value_type::second_type; +#endif template struct pair; diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h index b81b479ad43..00ec53ebc33 100644 --- a/libstdc++-v3/include/bits/stl_pair.h +++ b/libstdc++-v3/include/bits/stl_pair.h @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @cond undocumented // Forward declarations. + template + struct pair; + template class tuple; + // Declarations of std::array and its std::get overloads, so that + // std::tuple_cat can use them if is included before . + // We also declare the other std::get overloads here so that they're + // visible to the P2165R4 tuple-like constructors of pair and tuple. + template + struct array; + template struct _Index_tuple; + template + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& + get(pair<_Tp1, _Tp2>& __in) noexcept; + + template + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& + get(pair<_Tp1, _Tp2>&& __in) noexcept; + + template + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& + get(const pair<_Tp1, _Tp2>& __in) noexcept; + + template + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& + get(const pair<_Tp1, _Tp2>&& __in) noexcept; + + template + constexpr __tuple_element_t<__i, tuple<_Elements...>>& + get(tuple<_Elements...>& __t) noexcept; + + template + constexpr const __tuple_element_t<__i, tuple<_Elements...>>& + get(const tuple<_Elements...>& __t) noexcept; + + template + constexpr __tuple_element_t<__i, tuple<_Elements...>>&& + get(tuple<_Elements...>&& __t) noexcept; + + template + constexpr const __tuple_element_t<__i, tuple<_Elements...>>&& + get(const tuple<_Elements...>&& __t) noexcept; + + template + constexpr _Tp& + get(array<_Tp, _Nm>&) noexcept; + + template + constexpr _Tp&& + get(array<_Tp, _Nm>&&) noexcept; + + template + constexpr const _Tp& + get(const array<_Tp, _Nm>&) noexcept; + + template + constexpr const _Tp&& + get(const array<_Tp, _Nm>&&) noexcept; + #if ! __cpp_lib_concepts // Concept utility functions, reused in conditionally-explicit // constructors. @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif // lib concepts #endif // C++11 +#if __glibcxx_tuple_like // >= C++23 + template + inline constexpr bool __is_tuple_v = false; + + template + inline constexpr bool __is_tuple_v> = true; + + // TODO: Reuse __is_tuple_like from ? + template + inline constexpr bool __is_tuple_like_v = false; + + template + inline constexpr bool __is_tuple_like_v> = true; + + template + inline constexpr bool __is_tuple_like_v> = true; + + template + inline constexpr bool __is_tuple_like_v> = true; + + // __is_tuple_like_v is defined in . + + template + concept __tuple_like = __is_tuple_like_v>; + + template + concept __pair_like = __tuple_like<_Tp> && tuple_size_v> == 2; + + template + concept __eligible_tuple_like + = __detail::__different_from<_Tp, _Tuple> && __tuple_like<_Tp> + && (tuple_size_v> == tuple_size_v<_Tuple>) + && !ranges::__detail::__is_subrange>; + + template + concept __eligible_pair_like + = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp> + && !ranges::__detail::__is_subrange>; +#endif // C++23 + template class __pair_base { #if __cplusplus >= 201103L && ! __cpp_lib_concepts @@ -295,6 +393,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return false; #endif } + +#if __glibcxx_tuple_like // >= C++23 + template + static constexpr bool + _S_constructible_from_pair_like() + { + return _S_constructible(std::declval<_UPair>())), + decltype(std::get<1>(std::declval<_UPair>()))>(); + } + + template + static constexpr bool + _S_convertible_from_pair_like() + { + return _S_convertible(std::declval<_UPair>())), + decltype(std::get<1>(std::declval<_UPair>()))>(); + } + + template + static constexpr bool + _S_dangles_from_pair_like() + { + return _S_dangles(std::declval<_UPair>())), + decltype(std::get<1>(std::declval<_UPair>()))>(); + } +#endif // C++23 /// @endcond public: @@ -393,6 +517,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION pair(const pair<_U1, _U2>&&) = delete; #endif // C++23 +#if __glibcxx_tuple_like // >= C++23 + template<__eligible_pair_like _UPair> + requires (_S_constructible_from_pair_like<_UPair>()) + && (!_S_dangles_from_pair_like<_UPair>()) + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) + pair(_UPair&& __p) + : first(std::get<0>(std::forward<_UPair>(__p))), + second(std::get<1>(std::forward<_UPair>(__p))) + { } + + template<__eligible_pair_like _UPair> + requires (_S_constructible_from_pair_like<_UPair>()) + && (_S_dangles_from_pair_like<_UPair>()) + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) + pair(_UPair&&) = delete; +#endif // C++23 + private: /// @cond undocumented template @@ -421,6 +562,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return is_nothrow_assignable_v<_T2&, _U2>; return false; } + +#if __glibcxx_tuple_like // >= C++23 + template + static constexpr bool + _S_assignable_from_tuple_like() + { + return _S_assignable(std::declval<_UPair>())), + decltype(std::get<1>(std::declval<_UPair>()))>(); + } + + template + static constexpr bool + _S_const_assignable_from_tuple_like() + { + return _S_const_assignable(std::declval<_UPair>())), + decltype(std::get<1>(std::declval<_UPair>()))>(); + } +#endif // C++23 /// @endcond public: @@ -516,6 +675,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return *this; } #endif // C++23 + +#if __glibcxx_tuple_like // >= C++23 + template<__eligible_pair_like _UPair> + requires (_S_assignable_from_tuple_like<_UPair>()) + constexpr pair& + operator=(_UPair&& __p) + { + first = std::get<0>(std::forward<_UPair>(__p)); + second = std::get<1>(std::forward<_UPair>(__p)); + return *this; + } + + template<__eligible_pair_like _UPair> + requires (_S_const_assignable_from_tuple_like<_UPair>()) + constexpr const pair& + operator=(_UPair&& __p) const + { + first = std::get<0>(std::forward<_UPair>(__p)); + second = std::get<1>(std::forward<_UPair>(__p)); + return *this; + } +#endif // C++23 + #else // !__cpp_lib_concepts // C++11/14/17 implementation using enable_if, partially constexpr. diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h index d8a5fb960fe..2a741bf7000 100644 --- a/libstdc++-v3/include/bits/utility.h +++ b/libstdc++-v3/include/bits/utility.h @@ -266,6 +266,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif #endif +#if __glibcxx_ranges + namespace ranges::__detail + { + template + inline constexpr bool __is_subrange = false; + } // namespace __detail +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 8fb8a2877ee..502961eb269 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1780,6 +1780,14 @@ ftms = { }; }; +ftms = { + name = tuple_like; + values = { + v = 202207; + cxxmin = 23; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 9ba99deeda6..511030bde47 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2169,4 +2169,15 @@ #endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) */ #undef __glibcxx_want_generator +// from version.def line 1774 +#if !defined(__cpp_lib_tuple_like) +# if (__cplusplus >= 202100L) +# define __glibcxx_tuple_like 202207L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like) +# define __cpp_lib_tuple_like 202207L +# endif +# endif +#endif /* !defined(__cpp_lib_tuple_like) && defined(__glibcxx_want_tuple_like) */ +#undef __glibcxx_want_tuple_like + #undef __glibcxx_want_all diff --git a/libstdc++-v3/include/std/concepts b/libstdc++-v3/include/std/concepts index 66ed3714b25..4f3e059b051 100644 --- a/libstdc++-v3/include/std/concepts +++ b/libstdc++-v3/include/std/concepts @@ -62,6 +62,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION concept same_as = __detail::__same_as<_Tp, _Up> && __detail::__same_as<_Up, _Tp>; + namespace __detail + { + template + concept __different_from + = !same_as, remove_cvref_t<_Up>>; + } // namespace __detail + /// [concept.derived], concept derived_from template concept derived_from = __is_base_of(_Base, _Derived) @@ -185,8 +192,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template concept __adl_swap - = (__detail::__class_or_enum> - || __detail::__class_or_enum>) + = (std::__detail::__class_or_enum> + || std::__detail::__class_or_enum>) && requires(_Tp&& __t, _Up&& __u) { swap(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); }; diff --git a/libstdc++-v3/include/std/map b/libstdc++-v3/include/std/map index dcfd222d173..4a96e59a5bc 100644 --- a/libstdc++-v3/include/std/map +++ b/libstdc++-v3/include/std/map @@ -74,6 +74,7 @@ #define __glibcxx_want_map_try_emplace #define __glibcxx_want_node_extract #define __glibcxx_want_nonmember_container_access +#define __glibcxx_want_tuple_like #include #if __cplusplus >= 201703L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index f2413badd9c..7d739852677 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -2389,11 +2389,7 @@ namespace views::__adaptor inline constexpr bool __is_basic_string_view> = true; - template - inline constexpr bool __is_subrange = false; - - template - inline constexpr bool __is_subrange> = true; + using ranges::__detail::__is_subrange; template inline constexpr bool __is_iota_view = false; @@ -4166,6 +4162,10 @@ namespace views::__adaptor namespace __detail { +#if __cpp_lib_tuple_like // >= C++23 + template + concept __has_tuple_element = __tuple_like<_Tp> && _Nm < tuple_size_v<_Tp>; +#else template concept __has_tuple_element = requires(_Tp __t) { @@ -4175,6 +4175,7 @@ namespace views::__adaptor { std::get<_Nm>(__t) } -> convertible_to&>; }; +#endif template concept __returnable_element @@ -4559,23 +4560,12 @@ namespace views::__adaptor || (!(bidirectional_range<_Rs> && ...) && (common_range<_Rs> && ...)) || ((random_access_range<_Rs> && ...) && (sized_range<_Rs> && ...)); - template - struct __tuple_or_pair - { using type = std::tuple<_Ts...>; }; - - template - struct __tuple_or_pair<_Tp, _Up> - { using type = pair<_Tp, _Up>; }; - - template - using __tuple_or_pair_t = typename __tuple_or_pair<_Ts...>::type; - template constexpr auto __tuple_transform(_Fp&& __f, _Tuple&& __tuple) { return std::apply([&](_Ts&&... __elts) { - return __tuple_or_pair_t...> + return tuple...> (std::__invoke(__f, std::forward<_Ts>(__elts))...); }, std::forward<_Tuple>(__tuple)); } @@ -4696,7 +4686,7 @@ namespace views::__adaptor #ifdef __clang__ // LLVM-61763 workaround public: #endif - __detail::__tuple_or_pair_t>...> _M_current; + tuple>...> _M_current; constexpr explicit _Iterator(decltype(_M_current) __current) @@ -4728,7 +4718,7 @@ namespace views::__adaptor // iterator_category defined in __zip_view_iter_cat using iterator_concept = decltype(_S_iter_concept()); using value_type - = __detail::__tuple_or_pair_t>...>; + = tuple>...>; using difference_type = common_type_t>...>; @@ -4900,7 +4890,7 @@ namespace views::__adaptor template class zip_view<_Vs...>::_Sentinel { - __detail::__tuple_or_pair_t>...> _M_end; + tuple>...> _M_end; constexpr explicit _Sentinel(decltype(_M_end) __end) @@ -8325,8 +8315,7 @@ namespace views::__adaptor && __detail::__cartesian_product_is_common<_First, _Vs...>) { auto __its = [this](index_sequence<_Is...>) { - using _Ret = __detail::__tuple_or_pair_t, - iterator_t<_Vs>...>; + using _Ret = tuple, iterator_t<_Vs>...>; bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); auto& __first = std::get<0>(_M_bases); return _Ret{(__empty_tail @@ -8342,8 +8331,7 @@ namespace views::__adaptor end() const requires __detail::__cartesian_product_is_common { auto __its = [this](index_sequence<_Is...>) { - using _Ret = __detail::__tuple_or_pair_t, - iterator_t...>; + using _Ret = tuple, iterator_t...>; bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); auto& __first = std::get<0>(_M_bases); return _Ret{(__empty_tail @@ -8416,8 +8404,8 @@ namespace views::__adaptor { using _Parent = __maybe_const_t<_Const, cartesian_product_view>; _Parent* _M_parent = nullptr; - __detail::__tuple_or_pair_t>, - iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current; + tuple>, + iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current; constexpr _Iterator(_Parent& __parent, decltype(_M_current) __current) @@ -8444,11 +8432,11 @@ namespace views::__adaptor using iterator_category = input_iterator_tag; using iterator_concept = decltype(_S_iter_concept()); using value_type - = __detail::__tuple_or_pair_t>, - range_value_t<__maybe_const_t<_Const, _Vs>>...>; + = tuple>, + range_value_t<__maybe_const_t<_Const, _Vs>>...>; using reference - = __detail::__tuple_or_pair_t>, - range_reference_t<__maybe_const_t<_Const, _Vs>>...>; + = tuple>, + range_reference_t<__maybe_const_t<_Const, _Vs>>...>; using difference_type = decltype(cartesian_product_view::_S_difference_type()); _Iterator() = default; diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index be92f1eb973..ba364d6a4f8 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -50,6 +50,7 @@ #define __glibcxx_want_apply #define __glibcxx_want_make_from_tuple #define __glibcxx_want_ranges_zip +#define __glibcxx_want_tuple_like #include namespace std _GLIBCXX_VISIBILITY(default) @@ -246,6 +247,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Head _M_head_impl; }; +#if __cpp_lib_tuple_like // >= C++23 + struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; }; + + // These forward declarations are used by the operator<=> overload for + // tuple-like types. + template + constexpr _Cat + __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); + + template + constexpr _Cat + __tuple_cmp(const _Tp& __t, const _Up& __u, + index_sequence<_Idx0, _Idxs...>); +#endif // C++23 + /** * Contains the actual implementation of the @c tuple template, stored * as a recursive inheritance hierarchy from the first element (most @@ -342,6 +359,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<_Is...>) + : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...) + { } +#endif // C++23 + template _GLIBCXX20_CONSTEXPR _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) @@ -428,6 +453,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& __a, + _UTuple&& __u, index_sequence<_Is...>) + : _Tuple_impl(__tag, __a, std::get<_Is>(std::forward<_UTuple>(__u))...) + { } +#endif // C++23 + template _GLIBCXX20_CONSTEXPR void @@ -470,6 +504,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr void + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) + { + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); + } + + template + constexpr void + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const + { + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); + } +#endif // C++23 + protected: _GLIBCXX20_CONSTEXPR void @@ -563,6 +615,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<0>) + : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u))) + { } +#endif // C++23 + template _GLIBCXX20_CONSTEXPR _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) @@ -633,6 +693,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& __a, + _UTuple&& __u, index_sequence<0>) + : _Tuple_impl(__tag, __a, std::get<0>(std::forward<_UTuple>(__u))) + { } +#endif // C++23 + template _GLIBCXX20_CONSTEXPR void @@ -667,6 +736,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + constexpr void + _M_assign(__tuple_like_tag_t, _UTuple&& __u) + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } + + template + constexpr void + _M_assign(__tuple_like_tag_t, _UTuple&& __u) const + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } +#endif // C++23 + protected: _GLIBCXX20_CONSTEXPR void @@ -846,6 +927,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } +#if __cpp_lib_tuple_like // >= C++23 + template + static consteval bool + __dangles_from_tuple_like() + { + return [](index_sequence<_Is...>) { + return __dangles(std::declval<_UTuple>()))...>(); + }(index_sequence_for<_Elements...>{}); + } + + template + static consteval bool + __constructible_from_tuple_like() + { + return [](index_sequence<_Is...>) { + return __constructible(std::declval<_UTuple>()))...>(); + }(index_sequence_for<_Elements...>{}); + } + + template + static consteval bool + __convertible_from_tuple_like() + { + return [](index_sequence<_Is...>) { + return __convertible(std::declval<_UTuple>()))...>(); + }(index_sequence_for<_Elements...>{}); + } +#endif // C++23 + public: constexpr explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...)) @@ -1016,10 +1126,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(const pair<_U1, _U2>&&) = delete; #endif // C++23 -#if 0 && __cpp_lib_tuple_like // >= C++23 - template<__tuple_like _UTuple> - constexpr explicit(...) - tuple(_UTuple&& __u); +#if __cpp_lib_tuple_like // >= C++23 + template<__eligible_tuple_like _UTuple> + requires (__constructible_from_tuple_like<_UTuple>()) + && (!__use_other_ctor<_UTuple>()) + && (!__dangles_from_tuple_like<_UTuple>()) + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) + tuple(_UTuple&& __u) + : _Inherited(__tuple_like_tag_t{}, + std::forward<_UTuple>(__u), + index_sequence_for<_Elements...>{}) + { } + + template<__eligible_tuple_like _UTuple> + requires (__constructible_from_tuple_like<_UTuple>()) + && (!__use_other_ctor<_UTuple>()) + && (__dangles_from_tuple_like<_UTuple>()) + tuple(_UTuple&&) = delete; #endif // C++23 // Allocator-extended constructors. @@ -1202,10 +1325,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete; #endif // C++23 -#if 0 && __cpp_lib_tuple_like // >= C++23 - template - constexpr explicit(...) - tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u); +#if __cpp_lib_tuple_like // >= C++23 + template _UTuple> + requires (__constructible_from_tuple_like<_UTuple>()) + && (!__use_other_ctor<_UTuple>()) + && (!__dangles_from_tuple_like<_UTuple>()) + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u) + : _Inherited(__tuple_like_tag_t{}, + __tag, __a, std::forward<_UTuple>(__u), + index_sequence_for<_Elements...>{}) + { } + + template _UTuple> + requires (__constructible_from_tuple_like<_UTuple>()) + && (!__use_other_ctor<_UTuple>()) + && (__dangles_from_tuple_like<_UTuple>()) + tuple(allocator_arg_t, const _Alloc&, _UTuple&&) = delete; #endif // C++23 #else // !(concepts && conditional_explicit) @@ -1539,6 +1675,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif // C++23 +#if __cpp_lib_tuple_like // >= C++23 + template + static consteval bool + __assignable_from_tuple_like() + { + return [](index_sequence<_Is...>) { + return __assignable(std::declval<_UTuple>()))...>(); + }(index_sequence_for<_Elements...>{}); + } + + template + static consteval bool + __const_assignable_from_tuple_like() + { + return [](index_sequence<_Is...>) { + return __const_assignable(std::declval<_UTuple>()))...>(); + }(index_sequence_for<_Elements...>{}); + } +#endif // C++23 + public: tuple& operator=(const tuple& __u) = delete; @@ -1661,14 +1817,59 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif // C++23 -#if 0 && __cpp_lib_tuple_like // >= C++23 - template<__tuple_like _UTuple> +#if __cpp_lib_tuple_like // >= C++23 + template<__eligible_tuple_like _UTuple> + requires (__assignable_from_tuple_like<_UTuple>()) constexpr tuple& - operator=(_UTuple&& __u); + operator=(_UTuple&& __u) + { + this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u)); + return *this; + } + + template<__eligible_tuple_like _UTuple> + requires (__const_assignable_from_tuple_like<_UTuple>()) + constexpr const tuple& + operator=(_UTuple&& __u) const + { + this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u)); + return *this; + } template<__tuple_like _UTuple> - constexpr tuple& - operator=(_UTuple&& __u) const; + requires (!__is_tuple_v<_UTuple>) + friend constexpr bool + operator==(const tuple& __t, const _UTuple& __u) + { + static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>, + "tuple objects can only be compared if they have equal sizes."); + return [&](index_sequence<_Is...>) { + return (bool(std::get<_Is>(__t) == std::get<_Is>(__u)) + && ...); + }(index_sequence_for<_Elements...>{}); + } + + template<__tuple_like _UTuple, + typename = make_index_sequence>> + struct __tuple_like_common_comparison_category; + + template<__tuple_like _UTuple, size_t... _Is> + requires requires + { typename void_t<__detail::__synth3way_t<_Elements, tuple_element_t<_Is, _UTuple>>...>; } + struct __tuple_like_common_comparison_category<_UTuple, index_sequence<_Is...>> + { + using type = common_comparison_category_t + <__detail::__synth3way_t<_Elements, tuple_element_t<_Is, _UTuple>>...>; + }; + + template<__tuple_like _UTuple> + requires (!__is_tuple_v<_UTuple>) + friend constexpr typename __tuple_like_common_comparison_category<_UTuple>::type + operator<=>(const tuple& __t, const _UTuple& __u) + { + using _Cat = typename __tuple_like_common_comparison_category<_UTuple>::type; + return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Elements...>()); + } #endif // C++23 #else // ! (concepts && consteval) @@ -2433,27 +2634,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION forward_as_tuple(_Elements&&... __args) noexcept { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); } - // Declarations of std::array and its std::get overloads, so that - // std::tuple_cat can use them if is included before . - - template struct array; - - template - constexpr _Tp& - get(array<_Tp, _Nm>&) noexcept; - - template - constexpr _Tp&& - get(array<_Tp, _Nm>&&) noexcept; - - template - constexpr const _Tp& - get(const array<_Tp, _Nm>&) noexcept; - - template - constexpr const _Tp&& - get(const array<_Tp, _Nm>&&) noexcept; - /// @cond undocumented template struct __make_tuple_impl; @@ -2569,8 +2749,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @endcond /// Create a `tuple` containing all elements from multiple tuple-like objects +#if __cpp_lib_tuple_like // >= C++23 + template<__tuple_like... _Tpls> +#else template...>::value>::type> +#endif constexpr auto tuple_cat(_Tpls&&... __tpls) -> typename __tuple_cat_result<_Tpls...>::__type @@ -2722,7 +2906,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::get<_Idx>(std::forward<_Tuple>(__t))...); } +#if __cpp_lib_tuple_like // >= C++23 + template +#else template +#endif constexpr decltype(auto) apply(_Fn&& __f, _Tuple&& __t) noexcept(__unpack_std_tuple) @@ -2741,7 +2929,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>) { return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); } +#if __cpp_lib_tuple_like // >= C++23 + template +#else template +#endif constexpr _Tp make_from_tuple(_Tuple&& __t) noexcept(__unpack_std_tuple) @@ -2759,17 +2951,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif -#if __cpp_lib_ranges_zip // >= C++23 - template= C++23 + template<__tuple_like _TTuple, __tuple_like _UTuple, + template class _TQual, template class _UQual, + typename = make_index_sequence>> + struct __tuple_like_common_reference; + + template<__tuple_like _TTuple, __tuple_like _UTuple, + template class _TQual, template class _UQual, + size_t... _Is> + requires requires + { typename tuple>, + _UQual>>...>; } + struct __tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual, index_sequence<_Is...>> + { + using type = tuple>, + _UQual>>...>; + }; + + template<__tuple_like _TTuple, __tuple_like _UTuple, template class _TQual, template class _UQual> - requires requires { typename tuple, _UQual<_UTypes>>...>; } - struct basic_common_reference, tuple<_UTypes...>, _TQual, _UQual> - { using type = tuple, _UQual<_UTypes>>...>; }; - - template - requires requires { typename tuple...>; } - struct common_type, tuple<_UTypes...>> - { using type = tuple...>; }; + requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>) + && is_same_v<_TTuple, decay_t<_TTuple>> + && is_same_v<_UTuple, decay_t<_UTuple>> + && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>) + && requires { typename __tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual>::type; } + struct basic_common_reference<_TTuple, _UTuple, _TQual, _UQual> + { + using type = typename __tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual>::type; + }; + + template<__tuple_like _TTuple, __tuple_like _UTuple, + typename = make_index_sequence>> + struct __tuple_like_common_type; + + template<__tuple_like _TTuple, __tuple_like _UTuple, size_t... _Is> + requires requires + { typename tuple, + tuple_element_t<_Is, _UTuple>>...>; } + struct __tuple_like_common_type<_TTuple, _UTuple, index_sequence<_Is...>> + { + using type = tuple, + tuple_element_t<_Is, _UTuple>>...>; + }; + + template<__tuple_like _TTuple, __tuple_like _UTuple> + requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>) + && is_same_v<_TTuple, decay_t<_TTuple>> + && is_same_v<_UTuple, decay_t<_UTuple>> + && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>) + && requires { typename __tuple_like_common_type<_TTuple, _UTuple>::type; } + struct common_type<_TTuple, _UTuple> + { + using type = typename __tuple_like_common_type<_TTuple, _UTuple>::type; + }; #endif // C++23 /// @} diff --git a/libstdc++-v3/include/std/unordered_map b/libstdc++-v3/include/std/unordered_map index efad0cef584..ea6129d6494 100644 --- a/libstdc++-v3/include/std/unordered_map +++ b/libstdc++-v3/include/std/unordered_map @@ -51,6 +51,7 @@ #define __glibcxx_want_node_extract #define __glibcxx_want_nonmember_container_access #define __glibcxx_want_unordered_map_try_emplace +#define __glibcxx_want_tuple_like #include #if __cplusplus >= 201703L diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility index f113d572e59..212513f6f48 100644 --- a/libstdc++-v3/include/std/utility +++ b/libstdc++-v3/include/std/utility @@ -92,6 +92,7 @@ #define __glibcxx_want_tuple_element_t #define __glibcxx_want_tuples_by_type #define __glibcxx_want_unreachable +#define __glibcxx_want_tuple_like #include namespace std _GLIBCXX_VISIBILITY(default) diff --git a/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc new file mode 100644 index 00000000000..ef06df1c53f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc @@ -0,0 +1,173 @@ +// Verify P2165R4 enhancements to std::pair. +// { dg-do run { target c++23 } } + +#include +#include +#include +#include + +using std::array; +using std::pair; +using std::tuple; + +struct A { }; + +template class pair_like_t> +constexpr bool +test01() +{ + struct B { + int m; + constexpr B(A&) : m(0) { } + constexpr B(A&&) : m(1) { } + constexpr B(const A&) : m(2) { } + constexpr B(const A&&) : m(3) { } + }; + + // template + // constexpr explicit(false) pair(UPair&&); + + pair_like_t pair_like; + + [&] { + pair p2b = pair_like; + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); + }(); + [&] { + pair p2b = std::move(pair_like); + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); + }(); + [&] { + pair p2b = std::as_const(pair_like); + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); + }(); + [&] { + pair p2b = std::move(std::as_const(pair_like)); + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); + }(); + + // Verify dangling checks. + static_assert( !std::is_constructible_v, pair_like_t> ); + static_assert( !std::is_constructible_v, pair_like_t> ); + + return true; +} + +template class pair_like_t> +constexpr bool +test02() +{ + struct B { + int m; + constexpr explicit B(A&) : m(0) { } + constexpr explicit B(A&&) : m(1) { } + constexpr explicit B(const A&) : m(2) { } + constexpr explicit B(const A&&) : m(3) { } + }; + + // template + // constexpr explicit(true) pair(UPair&&); + + static_assert( !std::is_convertible_v, pair> ); + + pair_like_t pair_like; + + [&] { + pair p2b{pair_like}; + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); + }(); + [&] { + pair p2b{std::move(pair_like)}; + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); + }(); + [&] { + pair p2b{std::as_const(pair_like)}; + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); + }(); + [&] { + pair p2b{std::move(std::as_const(pair_like))}; + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); + }(); + + return true; +} + +template class pair_like_t> +constexpr bool +test03() +{ + struct B { + int m; + constexpr B& operator=(A&) { m = 0; return *this; } + constexpr B& operator=(A&&) { m = 1; return *this; } + constexpr B& operator=(const A&) { m = 2; return *this; } + constexpr B& operator=(const A&&) { m = 3; return *this; } + }; + + // template + // constexpr pair& operator=(UPair&&); + + pair_like_t pair_like; + + pair p2b; + p2b = pair_like; + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); + p2b = std::move(pair_like); + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); + p2b = std::as_const(pair_like); + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); + p2b = std::move(std::as_const(pair_like)); + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); + + return true; +} + +template class pair_like_t> +constexpr bool +test04() +{ + struct B { + mutable int m; + constexpr const B& operator=(A&) const { m = 0; return *this; } + constexpr const B& operator=(A&&) const { m = 1; return *this; } + constexpr const B& operator=(const A&) const { m = 2; return *this; } + constexpr const B& operator=(const A&&) const { m = 3; return *this; } + }; + + // template + // constexpr const pair& operator=(UPair&&) const; + + pair_like_t pair_like; + + const pair p2b; + p2b = pair_like; + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); + p2b = std::move(pair_like); + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); + p2b = std::as_const(pair_like); + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); + p2b = std::move(std::as_const(pair_like)); + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); + + return true; +} + +template +using pair_like_array = array; + +template +using pair_like_tuple = tuple; + +int +main() +{ + static_assert( test01() ); + static_assert( test02() ); + static_assert( test03() ); + static_assert( test04() ); + + static_assert( test01() ); + static_assert( test02() ); + static_assert( test03() ); + static_assert( test04() ); +} diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc new file mode 100644 index 00000000000..e2437c469b6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc @@ -0,0 +1,335 @@ +// Verify P2165R4 enhancements to std::tuple. +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +using std::array; +using std::pair; +using std::tuple; +using std::allocator; +using std::allocator_arg_t; +using std::allocator_arg; + +namespace alloc { + struct B01; + struct B02; +} + +template<> struct std::uses_allocator> : std::true_type { }; +template<> struct std::uses_allocator> : std::true_type { }; + +struct A { }; + +template class tuple_like_t> +constexpr bool +test01() +{ + struct B { + int m; + constexpr B(A&) : m(0) { } + constexpr B(A&&) : m(1) { } + constexpr B(const A&) : m(2) { } + constexpr B(const A&&) : m(3) { } + }; + + // template + // constexpr explicit(false) tuple(UTuple&&); + + tuple_like_t tuple_like; + + [&] { + tuple t3b = tuple_like; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + }(); + [&] { + tuple t3b = std::move(tuple_like); + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + }(); + [&] { + tuple t3b = std::as_const(tuple_like); + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + }(); + [&] { + tuple t3b = std::move(std::as_const(tuple_like)); + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + }(); + + // Verify dangling checks. + static_assert( !std::is_constructible_v, tuple_like_t> ); + static_assert( !std::is_constructible_v, tuple_like_t> ); + static_assert( !std::is_constructible_v, tuple_like_t> ); + + return true; +} + +namespace alloc +{ + struct B01 { + int m; + B01(A&); + B01(A&&); + B01(const A&); + B01(const A&&); + constexpr B01(allocator_arg_t, allocator, A&) : m(0) { } + constexpr B01(allocator_arg_t, allocator, A&&) : m(1) { } + constexpr B01(allocator_arg_t, allocator, const A&) : m(2) { } + constexpr B01(allocator_arg_t, allocator, const A&&) : m(3) { } + }; + + template class tuple_like_t> + constexpr bool + test01() + { + using B = B01; + + // template + // constexpr explicit(false) tuple(allocator_arg_t, const Alloc&, UTuple&&); + + tuple_like_t tuple_like; + + [&] { + tuple t3b = {allocator_arg, allocator{}, tuple_like}; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + }(); + [&] { + tuple t3b = {allocator_arg, allocator{}, std::move(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + }(); + [&] { + tuple t3b = {allocator_arg, allocator{}, std::as_const(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + }(); + [&] { + tuple t3b = {allocator_arg, allocator{}, std::move(std::as_const(tuple_like))}; + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + }(); + + // Verify dangling checks. + static_assert( !std::is_constructible_v, + allocator_arg_t, allocator, + tuple_like_t> ); + static_assert( !std::is_constructible_v, + allocator_arg_t, allocator, + tuple_like_t> ); + static_assert( !std::is_constructible_v, + allocator_arg_t, allocator, + tuple_like_t> ); + + return true; + } +} + +template class tuple_like_t> +constexpr bool +test02() +{ + struct B { + int m; + constexpr explicit B(A&) : m(0) { } + constexpr explicit B(A&&) : m(1) { } + constexpr explicit B(const A&) : m(2) { } + constexpr explicit B(const A&&) : m(3) { } + }; + + // template + // constexpr explicit(true) tuple(UTuple&&); + + static_assert( !std::is_convertible_v, tuple> ); + + tuple_like_t tuple_like; + + [&] { + tuple t3b{tuple_like}; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + }(); + [&] { + tuple t3b{std::move(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + }(); + [&] { + tuple t3b{std::as_const(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + }(); + [&] { + tuple t3b{std::move(std::as_const(tuple_like))}; + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + }(); + + return true; +} + +namespace alloc +{ + struct B02 { + int m; + explicit B02(A&); + explicit B02(A&&); + explicit B02(const A&); + explicit B02(const A&&); + explicit constexpr B02(allocator_arg_t, allocator, A&) : m(0) { } + explicit constexpr B02(allocator_arg_t, allocator, A&&) : m(1) { } + explicit constexpr B02(allocator_arg_t, allocator, const A&) : m(2) { } + explicit constexpr B02(allocator_arg_t, allocator, const A&&) : m(3) { } + }; + + template class tuple_like_t> + constexpr bool + test02() + { + using B = B02; + + // template + // constexpr explicit(true) tuple(allocator_arg_t, const Alloc&, UTuple&&); + + static_assert( !std::is_convertible_v, tuple> ); + + tuple_like_t tuple_like; + + [&] { + tuple t3b{allocator_arg, allocator{}, tuple_like}; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + }(); + [&] { + tuple t3b{allocator_arg, allocator{}, std::move(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + }(); + [&] { + tuple t3b{allocator_arg, allocator{}, std::as_const(tuple_like)}; + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + }(); + [&] { + tuple t3b{allocator_arg, allocator{}, std::move(std::as_const(tuple_like))}; + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + }(); + + return true; + } +} + + +template class tuple_like_t> +constexpr bool +test03() +{ + struct B { + int m; + constexpr B& operator=(A&) { m = 0; return *this; } + constexpr B& operator=(A&&) { m = 1; return *this; } + constexpr B& operator=(const A&) { m = 2; return *this; } + constexpr B& operator=(const A&&) { m = 3; return *this; } + }; + + // template + // constexpr tuple& operator=(UTuple&&); + + tuple_like_t tuple_like; + + tuple t3b; + t3b = tuple_like; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + t3b = std::move(tuple_like); + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + t3b = std::as_const(tuple_like); + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + t3b = std::move(std::as_const(tuple_like)); + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + + return true; +} + +template class tuple_like_t> +constexpr bool +test04() +{ + struct B { + mutable int m; + constexpr const B& operator=(A&) const { m = 0; return *this; } + constexpr const B& operator=(A&&) const { m = 1; return *this; } + constexpr const B& operator=(const A&) const { m = 2; return *this; } + constexpr const B& operator=(const A&&) const { m = 3; return *this; } + }; + + // template + // constexpr const tuple& operator=(UTuple&&) const; + + tuple_like_t tuple_like; + + const tuple t3b; + t3b = tuple_like; + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && std::get<2>(t3b).m == 0 ); + t3b = std::move(tuple_like); + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && std::get<2>(t3b).m == 1 ); + t3b = std::as_const(tuple_like); + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && std::get<2>(t3b).m == 2 ); + t3b = std::move(std::as_const(tuple_like)); + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && std::get<2>(t3b).m == 3 ); + + return true; +} + +template class tuple_like_t> +constexpr bool +test05() +{ + // template + // constexpr bool operator==(const tuple&, const UTuple&); + + static_assert( tuple{1, 2, 3} == tuple_like_t{1, 2, 3} ); + static_assert( tuple{1, 2, 4} != tuple_like_t{1, 2, 3} ); + static_assert( tuple_like_t{1, 2, 3} == tuple{1, 2, 3} ); + static_assert( tuple_like_t{1, 2, 3} != tuple{1, 2, 4} ); + + // template + // constexpr bool operator<=>const tuple&, const UTuple&); + + static_assert( (tuple{1, 2, 3} <=> tuple_like_t{1, 2, 3}) == std::strong_ordering::equal ); + static_assert( (tuple{1, 2, 4} <=> tuple_like_t{1, 2, 3}) == std::strong_ordering::greater ); + static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 3}) == std::strong_ordering::equal ); + static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 4}) == std::strong_ordering::less ); + + static_assert( tuple{1, 2, 4} > tuple_like_t{1, 2, 3} ); + static_assert( tuple_like_t{1, 2, 3} < tuple{1, 2, 4} ); + + // template + // struct basic_common_reference; + + static_assert( std::same_as, + tuple>, + tuple> ); + + static_assert( std::same_as, + tuple_like_t>, + tuple> ); + + // template + // struct common_type; + + static_assert( std::same_as, + tuple>, + tuple> ); + + static_assert( std::same_as, + tuple_like_t>, + tuple> ); + + return true; +} + +template +using tuple_like_array = array; + +int +main() +{ + static_assert( test01() ); + static_assert( alloc::test01() ); + static_assert( test02() ); + static_assert( alloc::test02() ); + static_assert( test03() ); + static_assert( test04() ); + static_assert( test05() ); +} diff --git a/libstdc++-v3/testsuite/std/ranges/zip/1.cc b/libstdc++-v3/testsuite/std/ranges/zip/1.cc index b7717aed92c..672a8c356d9 100644 --- a/libstdc++-v3/testsuite/std/ranges/zip/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/zip/1.cc @@ -41,8 +41,8 @@ test01() VERIFY( i2 == z2.end() ); VERIFY( ranges::size(z2) == 2 ); VERIFY( ranges::size(std::as_const(z2)) == 2 ); - VERIFY( z2[0].first == 1 && z2[0].second == 3 ); - VERIFY( z2[1].first == 2 && z2[1].second == 4 ); + VERIFY( std::get<0>(z2[0]) == 1 && std::get<1>(z2[0]) == 3 ); + VERIFY( std::get<0>(z2[1]) == 2 && std::get<1>(z2[1]) == 4 ); for (const auto [x, y] : z2) { VERIFY( y - x == 2 ); -- 2.43.0.386.ge02ecfcc53