diff --git a/stl/inc/ranges b/stl/inc/ranges index 1a435dccc7..b7e82235f2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5488,13 +5488,17 @@ namespace ranges { #endif // _HAS_CXX23 template - concept _Has_tuple_element = // + concept _Has_tuple_element = +#if _HAS_CXX23 + _Tuple_like<_Tuple> && _Index < tuple_size_v<_Tuple>; +#else // ^^^ C++23 / C++20 vvv requires(_Tuple __t) { typename tuple_size<_Tuple>::type; requires _Index < tuple_size_v<_Tuple>; typename tuple_element_t<_Index, _Tuple>; { _STD get<_Index>(__t) } -> convertible_to&>; }; +#endif // C++20 template concept _Returnable_element = is_reference_v<_Tuple> || move_constructible>; diff --git a/stl/inc/tuple b/stl/inc/tuple index 51bcd03fa9..de4ab09344 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -179,6 +179,43 @@ struct _Alloc_unpack_tuple_t { explicit _Alloc_unpack_tuple_t() = default; }; // tag type to disambiguate construction (from an allocator and unpacking a tuple/pair) +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template >> +inline constexpr bool _Can_construct_values_from_tuple_like_v = false; + +template +inline constexpr bool _Can_construct_values_from_tuple_like_v, _Other, index_sequence<_Indices...>> = + conjunction_v(_STD declval<_Other>()))>...>; + +#ifdef __clang__ // TRANSITION, LLVM-59827 +template +concept _Can_construct_from_tuple_like = _Tuple_like<_TupleLike> && _Different_from<_TupleLike, _Tuple> + && (!_Is_subrange_v>) + && (tuple_size_v<_Tuple> == tuple_size_v>) + && _Can_construct_values_from_tuple_like_v<_Tuple, _TupleLike> + && (tuple_size_v<_Tuple> != 1 + || (!is_convertible_v<_TupleLike, tuple_element_t<0, _Tuple>> + && !is_constructible_v, _TupleLike>) ); +#endif // __clang__ + +template >> +struct _Three_way_comparison_result_with_tuple_like {}; + +template + requires +#ifndef __clang__ // TRANSITION, DevCom-10265237 + (sizeof...(_TTypes) == sizeof...(_Indices)) && +#endif // __clang__ + (requires { typename _Synth_three_way_result<_TTypes, tuple_element_t<_Indices, _UTuple>>; } && ...) +struct _Three_way_comparison_result_with_tuple_like, _UTuple, index_sequence<_Indices...>> { + using type = common_comparison_category_t<_Synth_three_way_result<_TTypes, tuple_element_t<_Indices, _UTuple>>...>; +}; + +template +using _Three_way_comparison_result_with_tuple_like_t = + typename _Three_way_comparison_result_with_tuple_like<_TTuple, _UTuple>::type; +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + template <> class tuple<> { // empty tuple public: @@ -186,12 +223,24 @@ public: constexpr tuple(const tuple&) noexcept /* strengthened */ {} // TRANSITION, ABI: should be defaulted +#if _HAS_CXX23 && defined(__cpp_lib_concepts) + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (tuple_size_v> == 0) + constexpr tuple(_Other&&) noexcept /* strengthened */ {} +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + template _CONSTEXPR20 tuple(allocator_arg_t, const _Alloc&) noexcept /* strengthened */ {} template _CONSTEXPR20 tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept /* strengthened */ {} +#if _HAS_CXX23 && defined(__cpp_lib_concepts) + template + requires _Different_from<_Other, tuple> && (tuple_size_v> == 0) + constexpr tuple(allocator_arg_t, const _Alloc&, _Other&&) noexcept /* strengthened */ {} +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + template , int> = 0> constexpr tuple(_Tag) noexcept /* strengthened */ {} @@ -203,6 +252,20 @@ public: constexpr const tuple& operator=(const tuple&) const noexcept /* strengthened */ { return *this; } + +#ifdef __cpp_lib_concepts + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (tuple_size_v> == 0) + constexpr tuple& operator=(_Other&&) noexcept /* strengthened */ { + return *this; + } + + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (tuple_size_v> == 0) + constexpr const tuple& operator=(_Other&&) const noexcept /* strengthened */ { + return *this; + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 _CONSTEXPR20 void swap(tuple&) noexcept {} @@ -223,6 +286,26 @@ public: return false; } #endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +#if _HAS_CXX23 && defined(__cpp_lib_concepts) + template <_Tuple_like _Other> +#ifndef __clang__ // TRANSITION, DevCom-10265322 + requires _Different_from<_Other, tuple> +#endif // __clang__ + _NODISCARD_FRIEND constexpr bool operator==(const tuple&, const _Other&) noexcept /* strengthened */ { + static_assert(tuple_size_v<_Other> == 0, "Cannot compare tuples of different sizes (N4928 [tuple.rel]/2)."); + return true; + } + + template <_Tuple_like _Other> + requires (tuple_size_v> == 0) +#ifndef __clang__ // TRANSITION, DevCom-10265322 + && _Different_from<_Other, tuple> +#endif // __clang__ + _NODISCARD_FRIEND constexpr strong_ordering operator<=>(const tuple&, const _Other&) noexcept /* strengthened */ { + return strong_ordering::equal; + } +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) }; template @@ -343,6 +426,29 @@ public: tuple(const pair<_First, _Second>&& _Right) noexcept( _Tuple_nothrow_constructible_v) // strengthened : tuple(_Unpack_tuple_t{}, _STD move(_Right)) {} + +#ifdef __cpp_lib_concepts + template > + static constexpr bool _Is_tuple_like_constructor_explicit_v = false; + + template <_Tuple_like _Other, size_t... _Indices> + static constexpr bool _Is_tuple_like_constructor_explicit_v<_Other, index_sequence<_Indices...>> = + negation_v(_STD declval<_Other>())), _This>, + is_convertible(_STD declval<_Other>())), _Rest>...>>; + +#ifdef __clang__ // TRANSITION, LLVM-59827 + template , int> = 0> +#else // ^^^ workaround / no workaround vvv + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (!_Is_subrange_v>) + && (1 + sizeof...(_Rest) == tuple_size_v>) + && _Can_construct_values_from_tuple_like_v + && (sizeof...(_Rest) != 0 || (!is_convertible_v<_Other, _This> && !is_constructible_v<_This, _Other>) ) +#endif // __clang__ + constexpr explicit(_Is_tuple_like_constructor_explicit_v<_Other>) tuple(_Other&& _Right) + : tuple(_Unpack_tuple_t{}, _STD forward<_Other>(_Right)) { + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 template ) tuple(allocator_arg_t, const _Alloc& _Al, const pair<_First, _Second>&& _Right) : tuple(_Alloc_unpack_tuple_t{}, _Al, _STD move(_Right)) {} + +#ifdef __cpp_lib_concepts +#ifdef __clang__ // TRANSITION, LLVM-59827 + template , int> = 0> +#else // ^^^ workaround / no workaround vvv + template + requires _Different_from<_Other, tuple> && (!_Is_subrange_v>) + && (1 + sizeof...(_Rest) == tuple_size_v>) + && _Can_construct_values_from_tuple_like_v + && (sizeof...(_Rest) != 0 || (!is_convertible_v<_Other, _This> && !is_constructible_v<_This, _Other>) ) +#endif // __clang__ + constexpr explicit(_Is_tuple_like_constructor_explicit_v<_Other>) + tuple(allocator_arg_t, const _Alloc& _Al, _Other&& _Right) + : tuple(_Alloc_unpack_tuple_t{}, _Al, _STD forward<_Other>(_Right)) { + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 tuple& operator=(const volatile tuple&) = delete; @@ -570,6 +692,49 @@ public: _Get_rest()._Myfirst._Val = _STD forward<_Second>(_Right.second); return *this; } + +#ifdef __cpp_lib_concepts + template > + static constexpr bool _Can_assign_values_from_tuple_like_v = false; + + template <_Tuple_like _Other, size_t... _Indices> + static constexpr bool _Can_assign_values_from_tuple_like_v> = + conjunction_v(_STD declval<_Other>()))>, + is_assignable<_Rest&, decltype(_STD get<_Indices + 1>(_STD declval<_Other>()))>...>; + + template <_Tuple_like _Other, size_t... _Indices> + static constexpr bool _Can_assign_values_from_tuple_like_v> = + conjunction_v(_STD declval<_Other>()))>, + is_assignable(_STD declval<_Other>()))>...>; + + template <_Tuple_like _Other, size_t... _Indices> + constexpr void _Assign_tuple_like(_Other&& _Right, index_sequence<_Indices...>) { + ((void) (_STD get<_Indices>(*this) = _STD get<_Indices>(_STD forward<_Other>(_Right))), ...); + } + + template <_Tuple_like _Other, size_t... _Indices> + constexpr void _Assign_tuple_like(_Other&& _Right, index_sequence<_Indices...>) const { + ((void) (_STD get<_Indices>(*this) = _STD get<_Indices>(_STD forward<_Other>(_Right))), ...); + } + + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (!_Is_subrange_v>) + && (1 + sizeof...(_Rest) == tuple_size_v>) + && _Can_assign_values_from_tuple_like_v + constexpr tuple& operator=(_Other&& _Right) { + _Assign_tuple_like(_STD forward<_Other>(_Right), make_index_sequence<1 + sizeof...(_Rest)>{}); + return *this; + } + + template <_Tuple_like _Other> + requires _Different_from<_Other, tuple> && (!_Is_subrange_v>) + && (1 + sizeof...(_Rest) == tuple_size_v>) + && _Can_assign_values_from_tuple_like_v + constexpr const tuple& operator=(_Other&& _Right) const { + _Assign_tuple_like(_STD forward<_Other>(_Right), make_index_sequence<1 + sizeof...(_Rest)>{}); + return *this; + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 _CONSTEXPR20 void swap(tuple& _Right) noexcept( @@ -610,6 +775,56 @@ public: } return _Mybase::_Three_way_compare(_Right._Get_rest()); } + +#if _HAS_CXX23 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1753916 + template > + static constexpr bool _Can_equal_compare_with_tuple_like_v = false; + + template + static constexpr bool _Can_equal_compare_with_tuple_like_v<_Other, index_sequence<_Indices...>> = + (requires(const tuple& _Left, const _Other& _Right) { + { _STD get<_Indices>(_Left) == _STD get<_Indices>(_Right) } -> _Boolean_testable; + } && ...); +#endif // ^^^ no workaround ^^^ + + template + _NODISCARD constexpr bool _Equals_to_tuple_like(const _Other& _Right, index_sequence<_Indices...>) const { + return ((_STD get<_Indices>(*this) == _STD get<_Indices>(_Right)) && ...); + } + + template <_Tuple_like _Other> +#ifndef __clang__ // TRANSITION, DevCom-10265322 + requires _Different_from<_Other, tuple> +#endif // __clang__ + _NODISCARD_FRIEND constexpr bool operator==(const tuple& _Left, const _Other& _Right) { + static_assert(1 + sizeof...(_Rest) == tuple_size_v<_Other>, + "Cannot compare tuples of different sizes (N4928 [tuple.rel]/2)."); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1753916 + static_assert(_Can_equal_compare_with_tuple_like_v<_Other>, + "For all i, where 0 <= i < sizeof...(TTypes), get(t) == get(u) must be a valid expression (N4928 " + "[tuple.rel]/2)."); +#endif // ^^^ no workaround ^^^ + return _Left._Equals_to_tuple_like(_Right, make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + template + _NODISCARD constexpr auto _Three_way_compare_with_tuple_like( + const _Other& _Right, index_sequence<_Indices...>) const { + _Three_way_comparison_result_with_tuple_like_t _Result = strong_ordering::equal; + (void) (((_Result = _Synth_three_way{}(_STD get<_Indices>(*this), _STD get<_Indices>(_Right))) == 0) && ...); + return _Result; + } + + template <_Tuple_like _Other> +#ifndef __clang__ // TRANSITION, DevCom-10265322 + requires _Different_from<_Other, tuple> +#endif // __clang__ + _NODISCARD_FRIEND constexpr auto operator<=>(const tuple& _Left, const _Other& _Right) + -> _Three_way_comparison_result_with_tuple_like_t { + return _Left._Three_way_compare_with_tuple_like(_Right, make_index_sequence<1 + sizeof...(_Rest)>{}); + } +#endif // _HAS_CXX23 #else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD constexpr bool _Less(const tuple<_Other...>& _Right) const { @@ -667,7 +882,8 @@ tuple(allocator_arg_t, _Alloc, tuple<_Types...>) -> tuple<_Types...>; _EXPORT_STD template _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { - static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); + static_assert( + sizeof...(_Types1) == sizeof...(_Types2), "Cannot compare tuples of different sizes (N4928 [tuple.rel]/2)."); return _Left._Equals(_Right); } @@ -675,7 +891,6 @@ _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple _EXPORT_STD template _NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Types1, _Types2>...> operator<=>( const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { - static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); return _Left._Three_way_compare(_Right); } #else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv @@ -837,7 +1052,11 @@ struct _Tuple_cat2<_Ty, index_sequence<_Kx...>, index_sequence<_Ix...>, _Ix_next index_sequence<_Ix..., (_Ix_next + 0 * _Kx_next)...>, // repeat _Ix_next, ignoring the elements of _Kx_next _Ix_next + 1, _Rest...> {}; +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template <_Tuple_like... _Tuples> +#else // ^^^ C++23 / C++20 vvv template +#endif // C++20 using _Tuple_cat1 = _Tuple_cat2, index_sequence<>, index_sequence<>, 0, make_index_sequence>>...>; @@ -846,7 +1065,11 @@ constexpr _Ret _Tuple_cat(index_sequence<_Kx...>, index_sequence<_Ix...>, _Ty _A return _Ret{_STD get<_Kx>(_STD get<_Ix>(_STD move(_Arg)))...}; } +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +_EXPORT_STD template <_Tuple_like... _Tuples> +#else // ^^^ C++23 / C++20 vvv _EXPORT_STD template +#endif // C++20 _NODISCARD constexpr typename _Tuple_cat1<_Tuples...>::_Ret tuple_cat(_Tuples&&... _Tpls) { // concatenate tuples using _Cat1 = _Tuple_cat1<_Tuples...>; using _Ret = typename _Cat1::_Ret; @@ -856,13 +1079,21 @@ _NODISCARD constexpr typename _Tuple_cat1<_Tuples...>::_Ret tuple_cat(_Tuples&&. } #if _HAS_CXX17 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template +#else // ^^^ C++23 / C++20 vvv template +#endif // C++20 constexpr decltype(auto) _Apply_impl(_Callable&& _Obj, _Tuple&& _Tpl, index_sequence<_Indices...>) noexcept( noexcept(_STD invoke(_STD forward<_Callable>(_Obj), _STD get<_Indices>(_STD forward<_Tuple>(_Tpl))...))) { return _STD invoke(_STD forward<_Callable>(_Obj), _STD get<_Indices>(_STD forward<_Tuple>(_Tpl))...); } +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +_EXPORT_STD template +#else // ^^^ C++23 / C++20 vvv _EXPORT_STD template +#endif // C++20 constexpr decltype(auto) apply(_Callable&& _Obj, _Tuple&& _Tpl) noexcept( noexcept(_Apply_impl(_STD forward<_Callable>(_Obj), _STD forward<_Tuple>(_Tpl), make_index_sequence>>{}))) { @@ -870,7 +1101,11 @@ constexpr decltype(auto) apply(_Callable&& _Obj, _Tuple&& _Tpl) noexcept( make_index_sequence>>{}); } +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template +#else // ^^^ C++23 / C++20 vvv template +#endif // C++20 constexpr _Ty _Make_from_tuple_impl(_Tuple&& _Tpl, index_sequence<_Indices...>) noexcept( is_nothrow_constructible_v<_Ty, decltype(_STD get<_Indices>(_STD forward<_Tuple>(_Tpl)))...>) { // construct _Ty from the elements of _Tpl @@ -879,7 +1114,11 @@ constexpr _Ty _Make_from_tuple_impl(_Tuple&& _Tpl, index_sequence<_Indices...>) return _Ty(_STD get<_Indices>(_STD forward<_Tuple>(_Tpl))...); } +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +_EXPORT_STD template +#else // ^^^ C++23 / C++20 vvv _EXPORT_STD template +#endif // C++20 _NODISCARD constexpr _Ty make_from_tuple(_Tuple&& _Tpl) noexcept(noexcept(_Make_from_tuple_impl<_Ty>( _STD forward<_Tuple>(_Tpl), make_index_sequence>>{}))) /* strengthened */ { // construct _Ty from the elements of _Tpl @@ -891,19 +1130,52 @@ _NODISCARD constexpr _Ty make_from_tuple(_Tuple&& _Tpl) noexcept(noexcept(_Make_ template struct uses_allocator, _Alloc> : true_type {}; // true_type if container allocator enabled -#ifdef __cpp_lib_concepts -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>>...>; +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template <_Tuple_like _TTuple, _Tuple_like _UTuple, template class _TQual, template class _UQual, + class _Indices = make_index_sequence>> +struct _Tuple_like_common_reference; + +template class _TQual, template class _UQual, + size_t... _Indices> + requires requires { + typename tuple>, + _UQual>>...>; + } +struct _Tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual, index_sequence<_Indices...>> { + using type = tuple< + common_reference_t<_TQual>, _UQual>>...>; }; -template - requires requires { typename tuple...>; } -struct common_type, tuple<_UTypes...>> { - using type = tuple...>; +template <_Tuple_like _TTuple, _Tuple_like _UTuple, template class _TQual, template class _UQual> + requires (_Is_specialization_v<_TTuple, tuple> || _Is_specialization_v<_UTuple, tuple>) + && 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; }; -#endif // __cpp_lib_concepts + +template <_Tuple_like _TTuple, _Tuple_like _UTuple, class _Indices = make_index_sequence>> +struct _Tuple_like_common_type; + +template + requires requires { + typename tuple< + common_type_t, tuple_element_t<_Indices, _UTuple>>...>; + } +struct _Tuple_like_common_type<_TTuple, _UTuple, index_sequence<_Indices...>> { + using type = tuple, tuple_element_t<_Indices, _UTuple>>...>; +}; + +template <_Tuple_like _TTuple, _Tuple_like _UTuple> + requires (_Is_specialization_v<_TTuple, tuple> || _Is_specialization_v<_UTuple, tuple>) + && 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 // _HAS_CXX23 && defined(__cpp_lib_concepts) #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/utility b/stl/inc/utility index 35834b5711..1ad3d703bc 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -524,7 +524,7 @@ _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_ } #endif // ^^^ !defined(__cpp_lib_concepts) ^^^ -#ifdef __cpp_lib_concepts +#if _HAS_CXX23 && defined(__cpp_lib_concepts) template class _TQual, template class _UQual> requires requires { @@ -540,7 +540,7 @@ template struct common_type, pair<_Uty1, _Uty2>> { using type = pair, common_type_t<_Ty2, _Uty2>>; }; -#endif // __cpp_lib_concepts +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) template struct _Unrefwrap_helper { // leave unchanged if not a reference_wrapper diff --git a/stl/inc/xutility b/stl/inc/xutility index d4df0ef461..7478201040 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1096,14 +1096,30 @@ struct _Is_allocator<_Ty, void_t -using _Guide_key_t = remove_const_t::value_type::first_type>; +using _Guide_key_t = +#if _HAS_CXX23 + remove_const_t::value_type>>; +#else // ^^^ C++23 / C++20 vvv + remove_const_t::value_type::first_type>; +#endif // C++20 template -using _Guide_val_t = typename iterator_traits<_Iter>::value_type::second_type; +using _Guide_val_t = +#if _HAS_CXX23 + tuple_element_t<1, typename iterator_traits<_Iter>::value_type>; +#else // ^^^ C++23 / C++20 vvv + typename iterator_traits<_Iter>::value_type::second_type; +#endif // C++20 template -using _Guide_pair_t = pair::value_type::first_type>, - typename iterator_traits<_Iter>::value_type::second_type>; +using _Guide_pair_t = +#if _HAS_CXX23 + pair::value_type>>, + tuple_element_t<1, typename iterator_traits<_Iter>::value_type>>; +#else // ^^^ C++23 / C++20 vvv + pair::value_type::first_type>, + typename iterator_traits<_Iter>::value_type::second_type>; +#endif // C++20 _EXPORT_STD template struct is_execution_policy : false_type {}; @@ -3603,17 +3619,6 @@ namespace ranges { }; } // namespace ranges -// These declarations must be visible to qualified name lookup for _STD get in _Pair_like below, even if hasn't -// yet been included. -_EXPORT_STD template -_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; -_EXPORT_STD template -_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept; -_EXPORT_STD template -_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept; -_EXPORT_STD template -_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept; - namespace ranges { template concept _Uses_nonqualification_pointer_conversion = @@ -3624,6 +3629,7 @@ namespace ranges { concept _Convertible_to_non_slicing = convertible_to<_From, _To> && (!_Uses_nonqualification_pointer_conversion, decay_t<_To>>); +#if !_HAS_CXX23 template concept _Pair_like = (!is_reference_v<_Ty>) && requires(_Ty __t) { @@ -3634,9 +3640,14 @@ namespace ranges { { _STD get<0>(__t) } -> convertible_to&>; { _STD get<1>(__t) } -> convertible_to&>; }; +#endif // !_HAS_CXX23 template - concept _Pair_like_convertible_from = (!range<_Ty>) && _Pair_like<_Ty> && constructible_from<_Ty, _First, _Second> + concept _Pair_like_convertible_from = (!range<_Ty>) +#if _HAS_CXX23 + && (!is_reference_v<_Ty>) +#endif // _HAS_CXX23 + && _Pair_like<_Ty> && constructible_from<_Ty, _First, _Second> && _Convertible_to_non_slicing<_First, tuple_element_t<0, _Ty>> && convertible_to<_Second, tuple_element_t<1, _Ty>>; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 85bebe3ffb..15a3c3bdec 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -325,7 +325,6 @@ // P2077R3 Heterogeneous Erasure Overloads For Associative Containers // P2136R3 invoke_r() // P2165R4 Compatibility Between tuple, pair, And tuple-like Objects -// (changes to views::zip and pair only) // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr @@ -1738,7 +1737,12 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_string_contains 202011L #define __cpp_lib_string_resize_and_overwrite 202110L #define __cpp_lib_to_underlying 202102L -#define __cpp_lib_unreachable 202202L + +#ifdef __cpp_lib_concepts +#define __cpp_lib_tuple_like 202207L +#endif // __cpp_lib_concepts + +#define __cpp_lib_unreachable 202202L #endif // _HAS_CXX23 // macros with language mode sensitivity diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 261c92e5d5..cb9206752b 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -82,6 +82,9 @@ std/utilities/utility/mem.res/mem.res.pool/mem.res.pool.mem/unsync_deallocate_ma # Too many constexpr operations std/utilities/charconv/charconv.to.chars/integral.pass.cpp FAIL +# libc++ tests strengthened assignment operators (not compatible with P2165R4: "Compatibility Between tuple, pair, And tuple-like Objects") +std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL + # libc++ has not implemented P2505R5: "Monadic Functions for std::expected" std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp FAIL diff --git a/tests/std/test.lst b/tests/std/test.lst index 22e1b45d8f..9b1904a4cc 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -551,7 +551,12 @@ tests\P1899R3_views_stride_death tests\P1951R1_default_arguments_pair_forward_ctor tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant +tests\P2165R4_tuple_like_common_reference +tests\P2165R4_tuple_like_common_type +tests\P2165R4_tuple_like_operations tests\P2165R4_tuple_like_pair +tests\P2165R4_tuple_like_relational_operators +tests\P2165R4_tuple_like_tuple_members tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr tests\P2278R4_basic_const_iterator diff --git a/tests/std/tests/P0433R2_deduction_guides/test.cpp b/tests/std/tests/P0433R2_deduction_guides/test.cpp index 8b99de5f0b..e943019850 100644 --- a/tests/std/tests/P0433R2_deduction_guides/test.cpp +++ b/tests/std/tests/P0433R2_deduction_guides/test.cpp @@ -651,6 +651,20 @@ void test_map_or_multimap() { static_assert(is_same_v>); static_assert(is_same_v>>); static_assert(is_same_v, MyAlloc>>); + + { // Verify changes from P2165R4 + using TupleIter = tuple*; + static_assert(is_same_v>); + + using PairIter = pair*; + static_assert(is_same_v>); + + using ArrayIter = array*; + static_assert(is_same_v>); + + using SubrangeIter = ranges::subrange*; + static_assert(is_same_v>); + } #endif // _HAS_CXX23 && defined(__cpp_lib_concepts) } diff --git a/tests/std/tests/P2165R4_tuple_like_common_reference/env.lst b/tests/std/tests/P2165R4_tuple_like_common_reference/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_common_reference/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2165R4_tuple_like_common_reference/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_common_reference/test.compile.pass.cpp new file mode 100644 index 0000000000..b644d1d355 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_common_reference/test.compile.pass.cpp @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; +using ranges::subrange; + +template +concept CanCommonReference = requires { typename common_reference::type; }; + +template + requires CanCommonReference +constexpr bool test_common_reference = + same_as, Expected> && same_as, Expected>; + +struct Sentinel { + bool operator==(const auto&) const; // not defined + operator unreachable_sentinel_t() const; // not defined +}; + +template <> +struct std::common_type { + using type = unreachable_sentinel_t; +}; + +template <> +struct std::common_type { + using type = unreachable_sentinel_t; +}; + +static_assert(test_common_reference); + +// Test common_reference +static_assert(test_common_reference, tuple<>, tuple<>>); +static_assert(test_common_reference, tuple, tuple>); +static_assert(test_common_reference, tuple, tuple>); +static_assert(test_common_reference, tuple, tuple>); +static_assert(test_common_reference, tuple, tuple>); + +// Test common_reference +static_assert(test_common_reference&, tuple<>&&, const tuple<>&>); +static_assert( + test_common_reference&, volatile tuple&, const volatile tuple&>); +static_assert(test_common_reference&, + const tuple, tuple>); + +// Test common_reference, tuple-like is pair +static_assert(test_common_reference, pair, tuple>); +static_assert(test_common_reference, pair, tuple>); +static_assert( + test_common_reference, pair, tuple>); +static_assert(test_common_reference, pair, tuple>); + +// Test common_reference, tuple-like is pair +static_assert(test_common_reference&, pair&, tuple>); +static_assert(test_common_reference&, const pair&, tuple>); +static_assert( + test_common_reference&, const pair&, tuple>); +static_assert(test_common_reference, const pair&, + tuple>); + +// Test common_reference, tuple-like is array +static_assert(test_common_reference, array, tuple<>>); +static_assert(test_common_reference, array, tuple>); +static_assert(test_common_reference, array, tuple>); +static_assert(test_common_reference, array, tuple>); +static_assert(test_common_reference, array, tuple>); + +// Test common_reference, tuple-like is array +static_assert(test_common_reference&, array, tuple<>>); +static_assert(test_common_reference&, array&, tuple>); +static_assert(test_common_reference&&, array&, tuple>); + +// Test common_reference, tuple-like is ranges::subrange +static_assert(test_common_reference, subrange, tuple>); +static_assert( + test_common_reference, subrange, tuple>); +static_assert(test_common_reference, subrange, + tuple>); +static_assert(test_common_reference, subrange, + tuple>); + +// Test common_reference, tuple-like is ranges::subrange +static_assert(test_common_reference&&, const subrange, tuple>); +static_assert( + test_common_reference&, subrange, tuple>); + +// Test invalid common_references +static_assert(!CanCommonReference, array>); +static_assert(!CanCommonReference, subrange>); +static_assert(!CanCommonReference, pair>); diff --git a/tests/std/tests/P2165R4_tuple_like_common_type/env.lst b/tests/std/tests/P2165R4_tuple_like_common_type/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_common_type/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2165R4_tuple_like_common_type/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_common_type/test.compile.pass.cpp new file mode 100644 index 0000000000..79eca27f32 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_common_type/test.compile.pass.cpp @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; +using ranges::subrange; + +template +concept CanCommonType = requires { typename common_type::type; }; + +template + requires CanCommonType +inline constexpr bool test_common_type = + same_as, Expected> && same_as, Expected>; + +struct Sentinel { + bool operator==(const auto&) const; // not defined + operator unreachable_sentinel_t() const; // not defined +}; + +template <> +struct std::common_type { + using type = unreachable_sentinel_t; +}; + +template <> +struct std::common_type { + using type = unreachable_sentinel_t; +}; + +static_assert(test_common_type); + +// Test common_type +static_assert(test_common_type, tuple<>, tuple<>>); +static_assert(test_common_type, tuple, tuple>); +static_assert(test_common_type, tuple, tuple>); +static_assert(test_common_type, tuple, tuple>); +static_assert(test_common_type, tuple, tuple>); + +// Test common_reference +static_assert(test_common_type&, tuple<>&&, tuple<>>); +static_assert(test_common_type&, const tuple&&, tuple>); +static_assert(test_common_type&, tuple, tuple>); +static_assert(test_common_type&, tuple, tuple>); + +// Test common_type, tuple-like is pair +static_assert(test_common_type, pair, tuple>); +static_assert(test_common_type, pair, tuple>); +static_assert(test_common_type, pair, tuple>); +static_assert(test_common_type, pair, tuple>); + +// Test common_type, tuple-like is pair +static_assert(test_common_type&&, volatile pair&, tuple>); +static_assert(test_common_type&&, const pair&&, tuple>); + +// Test common_type, tuple-like is array +static_assert(test_common_type, array, tuple<>>); +static_assert(test_common_type, array, tuple>); +static_assert(test_common_type, array, tuple>); +static_assert(test_common_type, array, tuple>); +static_assert(test_common_type, array, tuple>); + +// Test common_type, tuple-like is array +static_assert(test_common_type&&, volatile array&, tuple<>>); +static_assert(test_common_type&, volatile array&&, tuple>); + +// Test common_type, tuple-like is ranges::subrange +static_assert(test_common_type, subrange, tuple>); +static_assert(test_common_type, subrange, tuple>); +static_assert(test_common_type, subrange, + tuple>); +static_assert(test_common_type, subrange, + tuple>); + +// Test common_type, tuple-like is ranges::subrange +static_assert(test_common_type&, volatile subrange&&, tuple>); +static_assert(test_common_type&, const subrange&&, + tuple>); + +// Test invalid common_types +static_assert(!CanCommonType, array>); +static_assert(!CanCommonType, subrange>); +static_assert(!CanCommonType, pair>); diff --git a/tests/std/tests/P2165R4_tuple_like_operations/env.lst b/tests/std/tests/P2165R4_tuple_like_operations/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_operations/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp new file mode 100644 index 0000000000..3f13d449cb --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_operations/test.compile.pass.cpp @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using ranges::subrange; + +namespace test_apply { + struct TakeAnythingFn { + void operator()(auto&&...) const noexcept; // not defined + }; + + struct StrSentinel { + constexpr bool operator==(const char*) const; // not defined + }; + + template + concept CanApply = requires(T tuple_like) { apply(TakeAnythingFn{}, tuple_like); }; + + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(CanApply>); + static_assert(!CanApply); + static_assert(!CanApply); + static_assert(!CanApply); + static_assert(!CanApply>); + + static constexpr int some_ints[] = {1, 2, 3}; + + constexpr bool test() { + { // Test apply with empty tuple-like types + true_type func; + assert(apply(func, tuple{})); + assert(apply(func, array{})); + } + + { // Test apply with one-sized tuple-like types + identity func; + assert(apply(func, tuple{7}) == 7); + assert(apply(func, array{7}) == 7); + } + + { // Test apply with pair-like types + minus func; + assert(apply(func, tuple{1, 1}) == 0); + assert(apply(func, pair{3, 3}) == 0); + assert(apply(func, array{5, 5}) == 0); + + constexpr subrange rng = some_ints; + assert(apply(func, rng) == -3); + } + + { // Test apply with big tuple-like types + auto func = [](auto&&... vals) { return (vals + ...); }; + assert(apply(func, tuple{1, 2, 3}) == 6); + assert(apply(func, array{4, 5, 6}) == 15); + } + + return true; + } +} // namespace test_apply + +namespace test_make_from_tuple { + template + concept CanMakeFromTuple = requires(Tuple tuple_like) { + { make_from_tuple(tuple_like) } -> same_as; + }; + + static_assert(CanMakeFromTuple>); + static_assert(CanMakeFromTuple>); + static_assert(CanMakeFromTuple>>); + static_assert(CanMakeFromTuple, pair>); + static_assert(CanMakeFromTuple>); + static_assert(CanMakeFromTuple, array>); + static_assert(CanMakeFromTuple, subrange>); + static_assert(CanMakeFromTuple, subrange>); + static_assert(!CanMakeFromTuple); + static_assert(!CanMakeFromTuple); + static_assert(!CanMakeFromTuple); + static_assert(!CanMakeFromTuple, int*>); + + constexpr bool test() { + { // Test make_from_tuple with empty tuple-like types + struct S { + constexpr bool operator==(const S&) const = default; + }; + + assert(make_from_tuple(tuple{}) == S{}); + assert(make_from_tuple(array{}) == S{}); + } + + { // Test make_from_tuple with one-sized tuple-like types + assert(make_from_tuple(tuple{7}) == 7); + assert(make_from_tuple(array{7}) == 7); + } + + { // Test make_from_tuple with pair-like types + struct S { + constexpr S(int* x_, int* y_) : x{x_}, y{y_} {} + constexpr bool operator==(const S&) const = default; + + int* x; + int* y; + }; + + int a = 0; + int b = 1; + S expected_val{&a, &b}; + + assert(make_from_tuple(tuple{&a, &b}) == expected_val); + assert(make_from_tuple(pair{&a, &b}) == expected_val); + assert(make_from_tuple(array{&a, &b}) == expected_val); + assert(make_from_tuple(subrange{&a, &b}) == expected_val); + } + + { // Test make_from_tuple with big tuple-like types + struct NoIncrement {}; + struct S { + constexpr S(NoIncrement, int x_, int y_, int z_) : x{x_}, y{y_}, z{z_} {} + constexpr S(int x_, int y_, int z_) : x{x_ + 1}, y{y_ + 1}, z{z_ + 1} {} + constexpr bool operator==(const S&) const = default; + + int x; + int y; + int z; + }; + + S expected_val{NoIncrement{}, 2, 3, 4}; + assert(make_from_tuple(tuple{1, 2, 3}) == expected_val); + assert(make_from_tuple(array{1, 2, 3}) == expected_val); + } + + return true; + } +} // namespace test_make_from_tuple + +namespace test_tuple_cat { + template + concept CanTupleCat = requires(Tuples... tpls) { tuple_cat(tpls...); }; + + template + concept CheckTupleCat = CanTupleCat && same_as()...)), Expected>; + + static_assert(CheckTupleCat, tuple<>, array>); + static_assert(CheckTupleCat, tuple, pair>); + static_assert(CheckTupleCat, array, subrange>); + static_assert(CheckTupleCat, array, pair>); + static_assert(CheckTupleCat, array, subrange>); + static_assert(!CanTupleCat); + static_assert(!CanTupleCat); + static_assert(!CanTupleCat>); + static_assert(!CanTupleCat>); + static_assert(!CanTupleCat, tuple, int>); + + constexpr bool test() { + // Test tuple_cat with empty tuple-like types + assert(tuple_cat(tuple{}, array{}) == tuple{}); + + // Test tuple_cat with one-sized tuple-like types + assert((tuple_cat(tuple{7}, array{8}) == tuple{7, 8})); + + { // Test tuple_cat with pair-like types + int a = 0; + int b = 1; + assert( + (tuple_cat(tuple{1, 2}, array{3, 4}, pair{5, 6}, subrange{&a, &b}) == tuple{1, 2, 3, 4, 5, 6, &a, &b})); + } + + // Test tuple_cat with big tuple-like types + assert((tuple_cat(tuple{1, 2, 3}, array{4, 5, 6}) == tuple{1, 2, 3, 4, 5, 6})); + + return true; + } +} // namespace test_tuple_cat + +static_assert(test_apply::test()); +static_assert(test_make_from_tuple::test()); +static_assert(test_tuple_cat::test()); diff --git a/tests/std/tests/P2165R4_tuple_like_relational_operators/env.lst b/tests/std/tests/P2165R4_tuple_like_relational_operators/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_relational_operators/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp new file mode 100644 index 0000000000..52d7a2c337 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_relational_operators/test.compile.pass.cpp @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using ranges::subrange; + +struct Incomparable {}; + +struct NeverCompare { + template + constexpr bool operator==(const NeverCompare&) const { + assert(false); + return true; + } + + template + constexpr auto operator<=>(const NeverCompare&) const { + assert(false); + return strong_ordering::equal; + } +}; + +template +concept verify_comparisons = equality_comparable_with && three_way_comparable_with; + +constexpr bool test() { + { // Check comparisons of empty tuple-like types + static_assert(verify_comparisons, array>); + static_assert(!verify_comparisons, array>); + + static_assert(tuple{} == array{}); + + constexpr same_as auto cmp = (tuple{} <=> array{}); + static_assert(is_eq(cmp)); + } + + { // Check comparisons of (tuple_size_v == 1) tuple-like types + static_assert(verify_comparisons, array>); + static_assert(!verify_comparisons, array>); + static_assert(tuple{5} == array{5}); + static_assert(tuple{6} != array{7}); + static_assert(is_eq(tuple{1} <=> array{1})); + static_assert(is_lt(tuple{1} <=> array{2})); + static_assert(is_gt(tuple{3} <=> array{2})); + } + + { // Check comparisons of (tuple_size_v == 2) tuple-like types + static_assert(verify_comparisons, array>); + static_assert(!verify_comparisons, array>); + static_assert(!verify_comparisons, array>); + static_assert(verify_comparisons, pair>); + static_assert(!verify_comparisons, pair>); + static_assert(!verify_comparisons, + subrange>); // subrange does not model equality_comparable + + static_assert(tuple{1, 2} == array{1, 2}); + static_assert(tuple{2, 1} != array{1, 2}); + static_assert(is_eq(tuple{1, 2} <=> array{1, 2})); + static_assert(is_lt(tuple{1, 1} <=> array{1, 2})); + static_assert(is_gt(tuple{2, 1} <=> array{1, 1})); + + static_assert(tuple{1, 2} == pair{1, 2}); + static_assert(tuple{2, 1} != pair{1, 2}); + static_assert(is_eq(tuple{1, 2} <=> pair{1, 2})); + static_assert(is_lt(tuple{1, 1} <=> pair{1, 2})); + static_assert(is_gt(tuple{2, 1} <=> pair{1, 1})); + + int a = 0; + int b = 1; + int c[2] = {2, 3}; + static_assert(tuple{&a, &b} == subrange{&a, &b}); + static_assert(tuple{&b, &a} != subrange{&a, &b}); + static_assert(is_eq(tuple{&a, &b} <=> pair{&a, &b})); + static_assert(is_lt(tuple{&c[0], &c[0]} <=> pair{&c[0], &c[1]})); + static_assert(is_gt(tuple{&c[1], &c[0]} <=> pair{&c[0], &c[1]})); + } + + { // Check comparisons of (tuple_size_v == 3) tuple-like types + static_assert(verify_comparisons, array>); + static_assert(!verify_comparisons, array>); + static_assert(tuple{1, 2, 3} == array{1, 2, 3}); + static_assert(tuple{6, 7, 8} != array{7, 8, 9}); + static_assert(is_eq(tuple{1, 2, 3} <=> array{1, 2, 3})); + static_assert(is_lt(tuple{1, 2, 3} <=> array{1, 2, 4})); + static_assert(is_gt(tuple{4, 3, 2} <=> array{3, 2, 1})); + } + + { // Check short circuit evaluation + static_assert(tuple{0, NeverCompare{}} != pair{1, NeverCompare{}}); + static_assert(is_lt(tuple{0, NeverCompare{}} <=> pair{1, NeverCompare{}})); + static_assert(is_gt(tuple{1, NeverCompare{}} <=> pair{0, NeverCompare{}})); + } + + { // Check result type of three-way comparison + struct NeedsToSynth3Way { + bool operator<(const NeedsToSynth3Way&) const; // not defined + }; + + static_assert(same_as array{0, 2})>); + static_assert(same_as array{NeedsToSynth3Way{}})>); + static_assert(same_as array{0, 2})>); + } + + { // Check incorrect three-way comparisons + static_assert(!three_way_comparable_with, array>); + static_assert(!three_way_comparable_with, subrange>); + static_assert(!three_way_comparable_with, pair>); + } + + return true; +} + +static_assert(test()); diff --git a/tests/std/tests/P2165R4_tuple_like_tuple_members/env.lst b/tests/std/tests/P2165R4_tuple_like_tuple_members/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_tuple_members/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2165R4_tuple_like_tuple_members/test.cpp b/tests/std/tests/P2165R4_tuple_like_tuple_members/test.cpp new file mode 100644 index 0000000000..b9ba3f2c4f --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_tuple_members/test.cpp @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template