From 65b4cba9d6a9ffe9b4d4bdff90727a7064cc0e3b Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 1 Feb 2024 14:59:46 -0500 Subject: libstdc++: Implement P2165R4 changes to std::pair/tuple/etc [PR113309] This implements the C++23 paper P2165R4 Compatibility between tuple, pair and tuple-like objects, which builds upon many changes from the earlier C++23 paper P2321R2 zip. Some declarations had to be moved around so that they're visible from without introducing new includes and bloating the header. In the end, the only new include is for from , for tuple_element_t. PR libstdc++/113309 PR libstdc++/109203 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. (test05): New test. * testsuite/20_util/pair/p2165r4.cc: New test. * testsuite/20_util/tuple/p2165r4.cc: New test. Reviewed-by: Jonathan Wakely --- 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 | 10 + libstdc++-v3/include/std/concepts | 11 +- libstdc++-v3/include/std/map | 1 + libstdc++-v3/include/std/ranges | 48 ++-- libstdc++-v3/include/std/tuple | 325 +++++++++++++++++++---- libstdc++-v3/include/std/unordered_map | 1 + libstdc++-v3/include/std/utility | 1 + libstdc++-v3/testsuite/20_util/pair/p2165r4.cc | 173 ++++++++++++ libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc | 335 ++++++++++++++++++++++++ libstdc++-v3/testsuite/std/ranges/zip/1.cc | 17 +- 15 files changed, 1070 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 (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index bb04c49..9b79c3a 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 d71a793..560a10a 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 b81b479..00ec53e 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 d8a5fb9..2a741bf 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 8fb8a28..502961e 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 fa4e89c..7a6fbd3 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1983,4 +1983,14 @@ #endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) */ #undef __glibcxx_want_generator +#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 66ed371..4f3e059 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 dcfd222..4a96e59 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 f2413ba..7d73985 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 be92f1e..9c89c13 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,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } +#if __cpp_lib_tuple_like // >= C++23 + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4045. tuple can create dangling references from tuple-like + 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 +1128,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 +1327,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 +1677,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 +1819,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 +2636,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 +2751,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 +2908,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 +2931,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 +2953,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 efad0ce..ea6129d 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 f113d572..212513f 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 0000000..ef06df1 --- /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 0000000..e2437c4 --- /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 b7717ae..ea4274d 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 ); @@ -124,6 +124,18 @@ test04() return true; } +constexpr bool +test05() +{ + // PR libstdc++/109203 + int x[] = {1, 1, 2}; + int y[] = {2, 1, 3}; + auto r = views::zip(x, y); + ranges::sort(r); + + return true; +} + int main() { @@ -131,4 +143,5 @@ main() static_assert(test02()); static_assert(test03()); static_assert(test04()); + static_assert(test05()); } -- cgit v1.1