diff --git a/stl/inc/__msvc_iter_core.hpp b/stl/inc/__msvc_iter_core.hpp index 5917ed1fb5..96efb43363 100644 --- a/stl/inc/__msvc_iter_core.hpp +++ b/stl/inc/__msvc_iter_core.hpp @@ -429,6 +429,9 @@ namespace ranges { _EXPORT_STD using ranges::get; +template +inline constexpr bool _Is_subrange_v> = true; + template struct tuple_size> : integral_constant {}; diff --git a/stl/inc/ranges b/stl/inc/ranges index 852004b8f9..19f422d78f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2713,11 +2713,6 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - template - inline constexpr bool _Is_subrange = false; - template - inline constexpr bool _Is_subrange> = true; - template concept _Random_sized_range = random_access_range<_Rng> && sized_range<_Rng>; @@ -2745,7 +2740,7 @@ namespace ranges { } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { return {_St::_Reconstruct_iota_view, noexcept(_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()))}; - } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange_v<_Ty>) { return {_St::_Reconstruct_subrange, noexcept(subrange(_RANGES begin(_STD declval<_Rng&>()), _RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>())))}; @@ -3121,7 +3116,7 @@ namespace ranges { return {_St::_Reconstruct_span, true}; } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { return {_St::_Reconstruct_other, true}; - } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange_v<_Ty>) { if constexpr (sized_sentinel_for, iterator_t<_Ty>>) { return {_St::_Reconstruct_subrange, noexcept(_Ty(_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), diff --git a/stl/inc/span b/stl/inc/span index 1969330380..6f59af0c54 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -222,9 +222,6 @@ struct _Span_extent_type<_Ty, dynamic_extent> { size_t _Mysize{0}; }; -_EXPORT_STD template -class array; - _EXPORT_STD template class span; @@ -242,12 +239,6 @@ inline constexpr bool _Is_span_v = false; template inline constexpr bool _Is_span_v> = true; -template -inline constexpr bool _Is_std_array_v = false; - -template -inline constexpr bool _Is_std_array_v> = true; - // clang-format off template concept _Span_compatible_iterator = contiguous_iterator<_It> diff --git a/stl/inc/tuple b/stl/inc/tuple index 4af9b88b44..cef9e93330 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -179,9 +179,6 @@ struct _Alloc_unpack_tuple_t { explicit _Alloc_unpack_tuple_t() = default; }; // tag type to disambiguate construction (from an allocator and unpacking a tuple/pair) -_EXPORT_STD template -class tuple; - template <> class tuple<> { // empty tuple public: @@ -824,21 +821,6 @@ _NODISCARD constexpr tuple<_Types&&...> forward_as_tuple(_Types&&... _Args) noex return tuple<_Types&&...>(_STD forward<_Types>(_Args)...); } -_EXPORT_STD template -class array; - -_EXPORT_STD template -_NODISCARD constexpr _Ty& get(array<_Ty, _Size>& _Arr) noexcept; - -_EXPORT_STD template -_NODISCARD constexpr const _Ty& get(const array<_Ty, _Size>& _Arr) noexcept; - -_EXPORT_STD template -_NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept; - -_EXPORT_STD template -_NODISCARD constexpr const _Ty&& get(const array<_Ty, _Size>&& _Arr) noexcept; - template struct _Tuple_cat2; diff --git a/stl/inc/utility b/stl/inc/utility index 9ddd014d84..8bbfd1b5a9 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -134,12 +134,87 @@ struct uses_allocator : _Has_allocator_type<_Ty, _Alloc>::type { _EXPORT_STD template _INLINE_VAR constexpr bool uses_allocator_v = uses_allocator<_Ty, _Alloc>::value; -_EXPORT_STD template +_EXPORT_STD template class tuple; +_EXPORT_STD template +struct pair; + +_EXPORT_STD template +class array; + +_EXPORT_STD template +struct tuple_size; + +_EXPORT_STD template +_INLINE_VAR constexpr size_t tuple_size_v = tuple_size<_Ty>::value; + +_EXPORT_STD template +struct tuple_element; + +_EXPORT_STD template +using tuple_element_t = typename tuple_element<_Index, _Tuple>::type; + _EXPORT_STD /* TRANSITION, VSO-1538698 */ template _NODISCARD constexpr auto&& _Tuple_get(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; + +_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 _Ty& get(array<_Ty, _Size>& _Arr) noexcept; + +_EXPORT_STD template +_NODISCARD constexpr const _Ty& get(const array<_Ty, _Size>& _Arr) noexcept; + +_EXPORT_STD template +_NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept; + +_EXPORT_STD template +_NODISCARD constexpr const _Ty&& get(const array<_Ty, _Size>&& _Arr) noexcept; + +#ifdef __cpp_lib_concepts +template +concept _Different_from = (!same_as, remove_cvref_t<_Ty2>>); + +template +inline constexpr bool _Is_std_array_v = false; + +template +inline constexpr bool _Is_std_array_v> = true; + +template +inline constexpr bool _Is_subrange_v = false; + +#if _HAS_CXX23 +template +inline constexpr bool _Tuple_like_impl = + _Is_specialization_v<_Ty, tuple> || _Is_specialization_v<_Ty, pair> || _Is_std_array_v<_Ty> || _Is_subrange_v<_Ty>; + +template +concept _Tuple_like = _Tuple_like_impl>; + +template +concept _Pair_like = _Tuple_like<_Ty> && tuple_size_v> == 2; + +#ifdef __clang__ // TRANSITION, LLVM-59827 +template +concept _Can_construct_from_pair_like = + _Pair_like<_PairLike> && is_constructible_v<_Ty1, decltype(_STD get<0>(_STD declval<_PairLike>()))> + && is_constructible_v<_Ty2, decltype(_STD get<1>(_STD declval<_PairLike>()))>; +#endif // __clang__ +#endif // _HAS_CXX23 +#endif // __cpp_lib_concepts + _EXPORT_STD template struct pair { // store a pair of values using first_type = _Ty1; @@ -207,6 +282,22 @@ struct pair { // store a pair of values pair(const pair<_Other1, _Other2>&& _Right) noexcept(is_nothrow_constructible_v<_Ty1, const _Other1>&& is_nothrow_constructible_v<_Ty2, const _Other2>) // strengthened : first(_STD forward(_Right.first)), second(_STD forward(_Right.second)) {} + +#ifdef __cpp_lib_concepts +#ifdef __clang__ // TRANSITION, LLVM-59827 + template , int> = 0> +#else // ^^^ workaround / no workaround vvv + template <_Pair_like _Other> + requires conjunction_v(_STD declval<_Other>()))>, + is_constructible<_Ty2, decltype(_STD get<1>(_STD declval<_Other>()))>> +#endif // __clang__ + constexpr explicit(!conjunction_v(_STD declval<_Other>())), _Ty1>, + is_convertible(_STD declval<_Other>())), _Ty2>>) + pair(_Other&& _Right) noexcept(is_nothrow_constructible_v<_Ty1, decltype(_STD get<0>(_STD declval<_Other>()))>&& + is_nothrow_constructible_v<_Ty2, decltype(_STD get<1>(_STD declval<_Other>()))>) // strengthened + : first(_STD get<0>(_STD forward<_Other>(_Right))), second(_STD get<1>(_STD forward<_Other>(_Right))) { + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 template @@ -318,6 +409,32 @@ struct pair { // store a pair of values second = _STD forward<_Other2>(_Right.second); return *this; } + +#ifdef __cpp_lib_concepts + template <_Pair_like _Other> + requires _Different_from<_Other, pair> && (!_Is_subrange_v>) + && is_assignable_v<_Ty1&, decltype(_STD get<0>(_STD declval<_Other>()))> + && is_assignable_v<_Ty2&, decltype(_STD get<1>(_STD declval<_Other>()))> + constexpr pair& operator=(_Other&& _Right) noexcept( + is_nothrow_assignable_v<_Ty1&, decltype(_STD get<0>(_STD declval<_Other>()))>&& + is_nothrow_assignable_v<_Ty2&, decltype(_STD get<1>(_STD declval<_Other>()))>) /* strengthened */ { + first = _STD get<0>(_STD forward<_Other>(_Right)); + second = _STD get<1>(_STD forward<_Other>(_Right)); + return *this; + } + + template <_Pair_like _Other> + requires _Different_from<_Other, pair> && (!_Is_subrange_v>) + && is_assignable_v(_STD declval<_Other>()))> + && is_assignable_v(_STD declval<_Other>()))> + constexpr const pair& operator=(_Other&& _Right) const noexcept( + is_nothrow_assignable_v(_STD declval<_Other>()))>&& + is_nothrow_assignable_v(_STD declval<_Other>()))>) /* strengthened */ { + first = _STD get<0>(_STD forward<_Other>(_Right)); + second = _STD get<1>(_STD forward<_Other>(_Right)); + return *this; + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX23 _CONSTEXPR20 void swap(pair& _Right) noexcept( @@ -469,9 +586,6 @@ namespace _CXX20_DEPRECATE_REL_OPS rel_ops { } } // namespace _CXX20_DEPRECATE_REL_OPS rel_ops -_EXPORT_STD template -struct tuple_size; - template struct _Tuple_size_sfinae {}; // selected when tuple_size<_Tuple>::value isn't well-formed @@ -488,12 +602,6 @@ struct _CXX20_DEPRECATE_VOLATILE tuple_size : _Tuple_size_sfina template struct _CXX20_DEPRECATE_VOLATILE tuple_size : _Tuple_size_sfinae<_Tuple> {}; // ignore cv -_EXPORT_STD template -_INLINE_VAR constexpr size_t tuple_size_v = tuple_size<_Ty>::value; - -_EXPORT_STD template -struct tuple_element; - template struct _MSVC_KNOWN_SEMANTICS tuple_element<_Index, const _Tuple> : tuple_element<_Index, _Tuple> { using _Mybase = tuple_element<_Index, _Tuple>; @@ -514,12 +622,6 @@ struct _CXX20_DEPRECATE_VOLATILE _MSVC_KNOWN_SEMANTICS tuple_element<_Index, con using type = add_cv_t; }; -_EXPORT_STD template -using tuple_element_t = typename tuple_element<_Index, _Tuple>::type; - -_EXPORT_STD template -class array; - template struct tuple_size> : integral_constant {}; // size of array diff --git a/stl/inc/xutility b/stl/inc/xutility index f59990c060..2aa727e0ff 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1755,9 +1755,6 @@ _NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept } #ifdef __cpp_lib_concepts -template -concept _Different_from = (!same_as, remove_cvref_t<_Ty2>>); - #if _HAS_CXX23 _EXPORT_STD template using iter_const_reference_t = common_reference_t&&, iter_reference_t<_Ty>>; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 541037dc0c..09d7e71fa2 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -322,7 +322,7 @@ // P2077R3 Heterogeneous Erasure Overloads For Associative Containers // P2136R3 invoke_r() // P2165R4 Compatibility Between tuple, pair, And tuple-like Objects -// (changes to views::zip only) +// (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 diff --git a/tests/std/test.lst b/tests/std/test.lst index 0a403a9eac..e22b1c2c6d 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -550,6 +550,7 @@ 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_pair tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr tests\P2278R4_basic_const_iterator diff --git a/tests/std/tests/P2165R4_tuple_like_pair/env.lst b/tests/std/tests/P2165R4_tuple_like_pair/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_pair/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_pair/test.cpp b/tests/std/tests/P2165R4_tuple_like_pair/test.cpp new file mode 100644 index 0000000000..d73f00f3a4 --- /dev/null +++ b/tests/std/tests/P2165R4_tuple_like_pair/test.cpp @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct TmpInt { + constexpr TmpInt() : TmpInt(0) {} + constexpr TmpInt(int v) : val{v} {} + + constexpr TmpInt(const TmpInt&) = default; + constexpr TmpInt(TmpInt&& other) : val{exchange(other.val, -1)} {} + + constexpr TmpInt& operator=(const TmpInt&) = default; + constexpr TmpInt& operator=(TmpInt&& other) { + if (this != &other) { + val = exchange(other.val, -1); + } + return *this; + } + + int val; + constexpr bool operator==(const TmpInt&) const = default; +}; + +template