diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 7b06dc31be7..a4b5675357d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -41,56 +41,12 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( const void* _First, const void* _Last, void* _Dest) noexcept; -const void* __stdcall __std_min_element_1(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_min_element_2(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_min_element_4(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_min_element_8(const void* _First, const void* _Last, bool _Signed) noexcept; - -const void* __stdcall __std_max_element_1(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_max_element_2(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_max_element_4(const void* _First, const void* _Last, bool _Signed) noexcept; -const void* __stdcall __std_max_element_8(const void* _First, const void* _Last, bool _Signed) noexcept; - _Min_max_element_t __stdcall __std_minmax_element_1(const void* _First, const void* _Last, bool _Signed) noexcept; _Min_max_element_t __stdcall __std_minmax_element_2(const void* _First, const void* _Last, bool _Signed) noexcept; _Min_max_element_t __stdcall __std_minmax_element_4(const void* _First, const void* _Last, bool _Signed) noexcept; _Min_max_element_t __stdcall __std_minmax_element_8(const void* _First, const void* _Last, bool _Signed) noexcept; _END_EXTERN_C -template -_Ty* __std_min_element(_Ty* _First, _Ty* _Last) noexcept { - constexpr bool _Signed = _STD is_signed_v<_Ty>; - - if constexpr (sizeof(_Ty) == 1) { - return const_cast<_Ty*>(static_cast(__std_min_element_1(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 2) { - return const_cast<_Ty*>(static_cast(__std_min_element_2(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 4) { - return const_cast<_Ty*>(static_cast(__std_min_element_4(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 8) { - return const_cast<_Ty*>(static_cast(__std_min_element_8(_First, _Last, _Signed))); - } else { - static_assert(_STD _Always_false<_Ty>, "Unexpected size"); - } -} - -template -_Ty* __std_max_element(_Ty* _First, _Ty* _Last) noexcept { - constexpr bool _Signed = _STD is_signed_v<_Ty>; - - if constexpr (sizeof(_Ty) == 1) { - return const_cast<_Ty*>(static_cast(__std_max_element_1(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 2) { - return const_cast<_Ty*>(static_cast(__std_max_element_2(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 4) { - return const_cast<_Ty*>(static_cast(__std_max_element_4(_First, _Last, _Signed))); - } else if constexpr (sizeof(_Ty) == 8) { - return const_cast<_Ty*>(static_cast(__std_max_element_8(_First, _Last, _Signed))); - } else { - static_assert(_STD _Always_false<_Ty>, "Unexpected size"); - } -} - template _STD pair<_Ty*, _Ty*> __std_minmax_element(_Ty* _First, _Ty* _Last) noexcept { constexpr bool _Signed = _STD is_signed_v<_Ty>; @@ -9351,259 +9307,6 @@ namespace ranges { #endif // __cpp_lib_concepts #endif // _HAS_CXX17 -template > -_INLINE_VAR constexpr bool _Is_min_max_optimization_safe = // Activate the vector algorithms for min_/max_element? - _Iterator_is_contiguous<_Iter> // The iterator must be contiguous so we can get raw pointers. - && !_Iterator_is_volatile<_Iter> // The iterator must not be volatile. - && conjunction_v, is_pointer<_Elem>>, // Element is of integral or pointer type. - disjunction< // And either of the following: -#ifdef __cpp_lib_concepts - is_same<_Pr, _RANGES less>, // predicate is ranges::less -#endif // __cpp_lib_concepts - is_same<_Pr, less<>>, is_same<_Pr, less<_Elem>>>>; // predicate is less - -template -constexpr _FwdIt _Max_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element -#if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) { - if (!_Is_constant_evaluated()) { - const auto _First_ptr = _To_address(_First); - const auto _Result = __std_max_element(_First_ptr, _To_address(_Last)); - if constexpr (is_pointer_v<_FwdIt>) { - return _Result; - } else { - return _First + (_Result - _First_ptr); - } - } - } -#endif // _USE_STD_VECTOR_ALGORITHMS - - _FwdIt _Found = _First; - if (_First != _Last) { - while (++_First != _Last) { - if (_DEBUG_LT_PRED(_Pred, *_Found, *_First)) { - _Found = _First; - } - } - } - - return _Found; -} - -_EXPORT_STD template -_NODISCARD constexpr _FwdIt max_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element - _Adl_verify_range(_First, _Last); - _Seek_wrapped(_First, _Max_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred))); - return _First; -} - -_EXPORT_STD template -_NODISCARD constexpr _FwdIt max_element(_FwdIt _First, _FwdIt _Last) { // find largest element - return _STD max_element(_First, _Last, less<>{}); -} - -#if _HAS_CXX17 -_EXPORT_STD template = 0> -_NODISCARD _FwdIt max_element(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Pr _Pred) noexcept /* terminates */ { - // find largest element - // not parallelized at present, parallelism expected to be feasible in a future release - return _STD max_element(_First, _Last, _Pass_fn(_Pred)); -} - -_EXPORT_STD template = 0> -_NODISCARD _FwdIt max_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* terminates */ { - // find largest element - // not parallelized at present, parallelism expected to be feasible in a future release - return _STD max_element(_First, _Last); -} - -#ifdef __cpp_lib_concepts -namespace ranges { - template - _NODISCARD constexpr _It _Max_element_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); - -#if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe<_It, _Pr> - && sized_sentinel_for<_Se, _It>) { - if (!_STD is_constant_evaluated()) { - const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + (_Last - _First); - const auto _Result = __std_max_element(_First_ptr, _Last_ptr); - if constexpr (is_pointer_v<_It>) { - return _Result; - } else { - return _First + (_Result - _First_ptr); - } - } - } -#endif // _USE_STD_VECTOR_ALGORITHMS - - auto _Found = _First; - if (_First == _Last) { - return _Found; - } - - while (++_First != _Last) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_Found), _STD invoke(_Proj, *_First))) { - _Found = _First; - } - } - - return _Found; - } - - class _Max_element_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template _Se, class _Pj = identity, - indirect_strict_weak_order> _Pr = ranges::less> - _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - _Seek_wrapped(_First, _RANGES _Max_element_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), - _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj))); - return _First; - } - - template , _Pj>> _Pr = ranges::less> - _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - _Seek_wrapped(_First, _RANGES _Max_element_unchecked(_Unwrap_range_iter<_Rng>(_STD move(_First)), - _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj))); - return _First; - } - }; - - _EXPORT_STD inline constexpr _Max_element_fn max_element{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts -#endif // _HAS_CXX17 - -template -constexpr _FwdIt _Min_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find smallest element -#if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) { - if (!_Is_constant_evaluated()) { - const auto _First_ptr = _To_address(_First); - const auto _Result = __std_min_element(_First_ptr, _To_address(_Last)); - if constexpr (is_pointer_v<_FwdIt>) { - return _Result; - } else { - return _First + (_Result - _First_ptr); - } - } - } -#endif // _USE_STD_VECTOR_ALGORITHMS - - _FwdIt _Found = _First; - if (_First != _Last) { - while (++_First != _Last) { - if (_DEBUG_LT_PRED(_Pred, *_First, *_Found)) { - _Found = _First; - } - } - } - - return _Found; -} - -_EXPORT_STD template -_NODISCARD constexpr _FwdIt min_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find smallest element - _Adl_verify_range(_First, _Last); - _Seek_wrapped(_First, _Min_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred))); - return _First; -} - -_EXPORT_STD template -_NODISCARD constexpr _FwdIt min_element(_FwdIt _First, _FwdIt _Last) { // find smallest element - return _STD min_element(_First, _Last, less<>{}); -} - -#if _HAS_CXX17 -_EXPORT_STD template = 0> -_NODISCARD _FwdIt min_element(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Pr _Pred) noexcept /* terminates */ { - // find smallest element - // not parallelized at present, parallelism expected to be feasible in a future release - return _STD min_element(_First, _Last, _Pass_fn(_Pred)); -} - -_EXPORT_STD template = 0> -_NODISCARD _FwdIt min_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* terminates */ { - // find smallest element - // not parallelized at present, parallelism expected to be feasible in a future release - return _STD min_element(_First, _Last); -} - -#ifdef __cpp_lib_concepts -namespace ranges { - template - _NODISCARD constexpr _It _Min_element_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); - -#if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe<_It, _Pr> - && sized_sentinel_for<_Se, _It>) { - if (!_STD is_constant_evaluated()) { - const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + (_Last - _First); - const auto _Result = __std_min_element(_First_ptr, _Last_ptr); - if constexpr (is_pointer_v<_It>) { - return _Result; - } else { - return _First + (_Result - _First_ptr); - } - } - } -#endif // _USE_STD_VECTOR_ALGORITHMS - - auto _Found = _First; - if (_First == _Last) { - return _Found; - } - - while (++_First != _Last) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Found))) { - _Found = _First; - } - } - - return _Found; - } - - class _Min_element_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template _Se, class _Pj = identity, - indirect_strict_weak_order> _Pr = ranges::less> - _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - _Seek_wrapped(_First, _RANGES _Min_element_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), - _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj))); - return _First; - } - - template , _Pj>> _Pr = ranges::less> - _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - _Seek_wrapped(_First, _RANGES _Min_element_unchecked(_Unwrap_range_iter<_Rng>(_STD move(_First)), - _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj))); - return _First; - } - }; - - _EXPORT_STD inline constexpr _Min_element_fn min_element{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts -#endif // _HAS_CXX17 - template constexpr pair<_FwdIt, _FwdIt> _Minmax_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { #if _USE_STD_VECTOR_ALGORITHMS @@ -9795,148 +9498,6 @@ namespace ranges { #endif // __cpp_lib_concepts #endif // _HAS_CXX17 -_EXPORT_STD template -_NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist, _Pr _Pred) { - // return leftmost/largest - const _Ty* _Res = _Max_element_unchecked(_Ilist.begin(), _Ilist.end(), _Pass_fn(_Pred)); - return *_Res; -} - -_EXPORT_STD template -_NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist) { - // return leftmost/largest - return (_STD max)(_Ilist, less<>{}); -} - -#ifdef __cpp_lib_concepts -namespace ranges { - template - concept _Prefer_iterator_copies = // When we have a choice, should we copy iterators or copy elements? - // pre: input_iterator<_It> - sizeof(_It) <= 2 * sizeof(iter_value_t<_It>) - && (is_trivially_copyable_v<_It> || !is_trivially_copyable_v>); - - class _Max_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template > _Pr = ranges::less> - _NODISCARD constexpr const _Ty& operator()( - const _Ty& _Left, const _Ty& _Right, _Pr _Pred = {}, _Pj _Proj = {}) const { - if (_STD invoke(_Pred, _STD invoke(_Proj, _Left), _STD invoke(_Proj, _Right))) { - return _Right; - } else { - return _Left; - } - } - - template > _Pr = ranges::less> - _NODISCARD constexpr _Ty operator()(initializer_list<_Ty> _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - const auto _First = _Range.begin(); - const auto _Last = _Range.end(); - _STL_ASSERT(_First != _Last, - "An initializer_list passed to std::ranges::max must not be empty. (N4861 [alg.min.max]/13)"); - return *_RANGES _Max_element_unchecked(_First, _Last, _Pass_fn(_Pred), _Pass_fn(_Proj)); - } - - template , _Pj>> _Pr = ranges::less> - requires indirectly_copyable_storable, range_value_t<_Rng>*> - _NODISCARD constexpr range_value_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - auto _UFirst = _Ubegin(_Range); - auto _ULast = _Uend(_Range); - _STL_ASSERT( - _UFirst != _ULast, "A range passed to std::ranges::max must not be empty. (N4861 [alg.min.max]/13)"); - if constexpr (forward_range<_Rng> && _Prefer_iterator_copies>) { - return static_cast>(*_RANGES _Max_element_unchecked( - _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj))); - } else { - range_value_t<_Rng> _Found(*_UFirst); - while (++_UFirst != _ULast) { - if (_STD invoke(_Pred, _STD invoke(_Proj, _Found), _STD invoke(_Proj, *_UFirst))) { - _Found = *_UFirst; - } - } - - return _Found; - } - } - }; - - _EXPORT_STD inline constexpr _Max_fn max{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts - -_EXPORT_STD template -_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist, _Pr _Pred) { - // return leftmost/smallest - const _Ty* _Res = _Min_element_unchecked(_Ilist.begin(), _Ilist.end(), _Pass_fn(_Pred)); - return *_Res; -} - -_EXPORT_STD template -_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist) { - // return leftmost/smallest - return (_STD min)(_Ilist, less<>{}); -} - -#ifdef __cpp_lib_concepts -namespace ranges { - class _Min_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template > _Pr = ranges::less> - _NODISCARD constexpr const _Ty& operator()( - const _Ty& _Left, const _Ty& _Right, _Pr _Pred = {}, _Pj _Proj = {}) const { - if (_STD invoke(_Pred, _STD invoke(_Proj, _Right), _STD invoke(_Proj, _Left))) { - return _Right; - } else { - return _Left; - } - } - - template > _Pr = ranges::less> - _NODISCARD constexpr _Ty operator()(initializer_list<_Ty> _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - const auto _First = _Range.begin(); - const auto _Last = _Range.end(); - _STL_ASSERT(_First != _Last, - "An initializer_list passed to std::ranges::min must not be empty. (N4861 [alg.min.max]/5)"); - return *_RANGES _Min_element_unchecked(_First, _Last, _Pass_fn(_Pred), _Pass_fn(_Proj)); - } - - template , _Pj>> _Pr = ranges::less> - requires indirectly_copyable_storable, range_value_t<_Rng>*> - _NODISCARD constexpr range_value_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - auto _UFirst = _Ubegin(_Range); - auto _ULast = _Uend(_Range); - _STL_ASSERT( - _UFirst != _ULast, "A range passed to std::ranges::min must not be empty. (N4861 [alg.min.max]/5)"); - if constexpr (forward_range<_Rng> && _Prefer_iterator_copies>) { - return static_cast>(*_RANGES _Min_element_unchecked( - _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj))); - } else { - range_value_t<_Rng> _Found(*_UFirst); - while (++_UFirst != _ULast) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, _Found))) { - _Found = *_UFirst; - } - } - - return _Found; - } - } - }; - - _EXPORT_STD inline constexpr _Min_fn min{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts - _EXPORT_STD template _NODISCARD constexpr pair minmax(const _Ty& _Left, const _Ty& _Right, _Pr _Pred) noexcept( noexcept(_DEBUG_LT_PRED(_Pred, _Right, _Left))) /* strengthened */ { diff --git a/stl/inc/ranges b/stl/inc/ranges index c6889745c0d..1c76f60f635 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -7049,6 +7049,495 @@ namespace ranges { _EXPORT_STD inline constexpr _Stride_fn stride; } // namespace views + template + concept _Zip_is_common = (sizeof...(_RangeTypes) == 1 && (common_range<_RangeTypes> && ...)) // + || (!(bidirectional_range<_RangeTypes> && ...) && (common_range<_RangeTypes> && ...)) // + || ((random_access_range<_RangeTypes> && ...) && (sized_range<_RangeTypes> && ...)); + + template + constexpr auto _Tuple_transform_closure(_CallbackType& _Callback) noexcept { + return [&_Callback](_ViewTupleTypes&&... _View_tuples) noexcept( + noexcept(tuple...>{ + _STD invoke(_Callback, _STD forward<_ViewTupleTypes>(_View_tuples))...})) { + return tuple...>{ + _STD invoke(_Callback, _STD forward<_ViewTupleTypes>(_View_tuples))...}; + }; + } + + template + constexpr auto _Tuple_transform(_CallbackType&& _Callback, _TupleType&& _Tuple) noexcept(noexcept( + _STD apply(_Tuple_transform_closure(_Callback), _STD forward<_TupleType>(_Tuple)))) /* strengthened */ { + return _STD apply(_Tuple_transform_closure(_Callback), _STD forward<_TupleType>(_Tuple)); + } + + template + constexpr auto _Tuple_for_each_closure(_CallbackType& _Callback) noexcept { + return [&_Callback](_ViewTupleTypes&&... _View_tuples) noexcept( + noexcept(((void) (_STD invoke(_Callback, _STD forward<_ViewTupleTypes>(_View_tuples))), ...))) { + ((void) (_STD invoke(_Callback, _STD forward<_ViewTupleTypes>(_View_tuples))), ...); + }; + } + + template + constexpr void _Tuple_for_each(_CallbackType&& _Callback, _TupleType&& _Tuple) noexcept( + noexcept(_STD apply(_Tuple_for_each_closure(_Callback), _STD forward<_TupleType>(_Tuple)))) /* strengthened */ { + _STD apply(_Tuple_for_each_closure(_Callback), _STD forward<_TupleType>(_Tuple)); + } + + template + concept _All_random_access = (random_access_range<_Maybe_const<_IsConst, _Views>> && ...); + + template + concept _All_bidirectional = (bidirectional_range<_Maybe_const<_IsConst, _Views>> && ...); + + template + concept _All_forward = (forward_range<_Maybe_const<_IsConst, _Views>> && ...); + + template + requires (sizeof...(_LHSTupleTypes) == sizeof...(_RHSTupleTypes)) + _NODISCARD constexpr _ResultType _Zip_get_smallest_distance( + const tuple<_LHSTupleTypes...>& _Lhs_tuple, const tuple<_RHSTupleTypes...>& _Rhs_tuple) + // clang-format off + noexcept((noexcept(static_cast<_ResultType>( + _STD declval() - _STD declval())) && ...)) { + constexpr bool _Is_noexcept = (noexcept(static_cast<_ResultType>( + _STD declval() - _STD declval())) && ...); + // clang-format on + const auto _Get_smallest_distance_closure = + [&_Lhs_tuple, &_Rhs_tuple ]( + index_sequence<_FirstIdx, _Idxs...>) noexcept(_Is_noexcept) { + const _ResultType _First_size = static_cast<_ResultType>(_STD get<0>(_Lhs_tuple) - _STD get<0>(_Rhs_tuple)); + + if (_First_size == 0) { + return static_cast<_ResultType>(0); + } + const initializer_list<_ResultType> _Sizes = { + _First_size, (_STD get<_Idxs>(_Lhs_tuple) - _STD get<_Idxs>(_Rhs_tuple))...}; + return _First_size < 0 ? static_cast<_ResultType>((_RANGES max)(_Sizes)) + : static_cast<_ResultType>((_RANGES min)(_Sizes)); + }; + + return _Get_smallest_distance_closure(index_sequence_for<_LHSTupleTypes...>{}); + } + + template + requires (sizeof...(_LHSTupleTypes) == sizeof...(_RHSTupleTypes)) + _NODISCARD constexpr bool _Zip_iterator_sentinel_equal( + const tuple<_LHSTupleTypes...>& _Lhs_tuple, const tuple<_RHSTupleTypes...>& _Rhs_tuple) + // clang-format off + noexcept((noexcept(_STD declval() == _STD declval()) && ...)) { + // clang-format on + const auto _Evaluate_equality_closure = + [&_Lhs_tuple, &_Rhs_tuple ](index_sequence<_Indices...>) noexcept( + (noexcept(_STD declval() == _STD declval()) && ...)) { + return ((_STD get<_Indices>(_Lhs_tuple) == _STD get<_Indices>(_Rhs_tuple)) || ...); + }; + + return _Evaluate_equality_closure(index_sequence_for<_LHSTupleTypes...>{}); + } + + _EXPORT_STD template + requires (view<_ViewTypes> && ...) && (sizeof...(_ViewTypes) > 0) + class zip_view : public view_interface> { + private: + /* [[no_unique_address]] */ tuple<_ViewTypes...> _Views; + + template + struct _Category_base {}; + + template + requires _All_forward<_IsConst, _ViewTypes...> + struct _Category_base<_IsConst> { + using iterator_category = input_iterator_tag; + }; + + template + class _Iterator : public _Category_base<_IsConst> { + private: + friend zip_view; + + using _My_tuple = tuple>...>; + + /* [[no_unique_address]] */ _My_tuple _Current; + + constexpr explicit _Iterator(_My_tuple _Current_) noexcept( + is_nothrow_move_constructible_v<_My_tuple>) // strengthened + : _Current(_STD move(_Current_)) {} + + public: + using iterator_concept = conditional_t<_All_random_access<_IsConst, _ViewTypes...>, + random_access_iterator_tag, + conditional_t<_All_bidirectional<_IsConst, _ViewTypes...>, bidirectional_iterator_tag, + conditional_t<_All_forward<_IsConst, _ViewTypes...>, forward_iterator_tag, input_iterator_tag>>>; + + using value_type = tuple>...>; + using difference_type = common_type_t>...>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _Rhs) noexcept( + (is_nothrow_convertible_v, iterator_t> && ...)) // strengthened + requires (_IsConst && (convertible_to, iterator_t> && ...)) + : _Current(_STD move(_Rhs._Current)) {} + + _NODISCARD constexpr auto operator*() const + noexcept((noexcept(*(_STD declval>&>())) + && ...)) /* strengthened */ { + return _Tuple_transform( + [](auto& _Itr) noexcept(noexcept(*_Itr)) -> decltype(auto) { return *_Itr; }, _Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept( + _Tuple_for_each([](auto& _Itr) noexcept(noexcept(++_Itr)) { ++_Itr; }, _Current))) /* strengthened */ { + _Tuple_for_each([](auto& _Itr) noexcept(noexcept(++_Itr)) { ++_Itr; }, _Current); + return *this; + } + + constexpr void operator++(int) noexcept(noexcept(++(_STD declval<_Iterator>()))) /* strengthened */ { + ++*this; + } + + constexpr _Iterator operator++(int) noexcept( + noexcept(++(_STD declval<_Iterator>())) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires _All_forward<_IsConst, _ViewTypes...> + { + auto _Temp = *this; + ++*this; + + return _Temp; + } + + constexpr _Iterator& operator--() noexcept(noexcept( + _Tuple_for_each([](auto& _Itr) noexcept(noexcept(--_Itr)) { --_Itr; }, _Current))) // strengthened + requires _All_bidirectional<_IsConst, _ViewTypes...> + { + _Tuple_for_each([](auto& _Itr) noexcept(noexcept(--_Itr)) { --_Itr; }, _Current); + return *this; + } + + constexpr _Iterator operator--(int) noexcept( + noexcept(--(_STD declval<_Iterator>())) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires _All_bidirectional<_IsConst, _ViewTypes...> + { + auto _Temp = *this; + --*this; + + return _Temp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) noexcept( + (noexcept(_STD declval>&>() += + static_cast>>>(_Off)) + && ...)) // strengthened + requires _All_random_access<_IsConst, _ViewTypes...> + { + _Tuple_for_each( + [&_Off](_IteratorType& _Itr) noexcept( + noexcept(_Itr += static_cast>(_Off))) { + _Itr += static_cast>(_Off); + }, + _Current); + + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) noexcept( + (noexcept(_STD declval>&>() -= + static_cast>>>(_Off)) + && ...)) // strengthened + requires _All_random_access<_IsConst, _ViewTypes...> + { + _Tuple_for_each( + [&_Off](_IteratorType& _Itr) noexcept( + noexcept(_Itr -= static_cast>(_Off))) { + _Itr -= static_cast>(_Off); + }, + _Current); + + return *this; + } + + _NODISCARD constexpr auto operator[](const difference_type _Where) const noexcept( + (noexcept((_STD declval>&>()) + [static_cast>>>(_Where)]) + && ...)) // strengthened + requires _All_random_access<_IsConst, _ViewTypes...> + { + return _Tuple_transform( + [&_Where](_IteratorType& _Itr) noexcept( + noexcept(_Itr[static_cast>(_Where)])) -> decltype(auto) { + return _Itr[static_cast>(_Where)]; + }, + _Current); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Lhs, const _Iterator& _Rhs) noexcept( + noexcept(_Zip_iterator_sentinel_equal(_Lhs._Current, _Rhs._Current))) // strengthened + requires (equality_comparable>> && ...) + { + if constexpr (!_Zip_is_common<_Maybe_const<_IsConst, _ViewTypes>...> + || _All_random_access<_IsConst, _ViewTypes...> || sizeof...(_ViewTypes) == 1) { + return _STD get<0>(_Lhs._Current) == _STD get<0>(_Rhs._Current); + } else { + return _Zip_iterator_sentinel_equal(_Lhs._Current, _Rhs._Current); + } + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Lhs, const _Iterator& _Rhs) + requires _All_random_access<_IsConst, _ViewTypes...> + { + return _Lhs._Current <=> _Rhs._Current; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _Lhs, const difference_type _Rhs) noexcept( + noexcept(_STD declval<_Iterator&>() += _Rhs) + && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires _All_random_access<_IsConst, _ViewTypes...> + { + auto _Modified_iterator = _Lhs; + _Modified_iterator += _Rhs; + + return _Modified_iterator; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Lhs, const _Iterator& _Rhs) noexcept( + noexcept(_Rhs + _Lhs)) /* strengthened */ + requires _All_random_access<_IsConst, _ViewTypes...> + { + return _Rhs + _Lhs; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _Lhs, const difference_type _Rhs) noexcept( + noexcept(_STD declval<_Iterator&>() -= _Rhs) + && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires _All_random_access<_IsConst, _ViewTypes...> + { + auto _Modified_iterator = _Lhs; + _Modified_iterator -= _Rhs; + + return _Modified_iterator; + } + + _NODISCARD_FRIEND constexpr difference_type + operator-(const _Iterator& _Lhs, const _Iterator& _Rhs) noexcept( + noexcept(_Zip_get_smallest_distance(_STD declval<_My_tuple>(), + _STD declval<_My_tuple>()))) // strengthened + requires (sized_sentinel_for>, + iterator_t<_Maybe_const<_IsConst, _ViewTypes>>> + && ...) + { + return _Zip_get_smallest_distance(_Lhs._Current, _Rhs._Current); + } + + _NODISCARD_FRIEND constexpr auto iter_move(const _Iterator& _Itr) noexcept( + (noexcept(_RANGES iter_move(_STD declval>&>())) + && ...) + && (is_nothrow_move_constructible_v>> + && ...)) { + return _Tuple_transform(_RANGES iter_move, _Itr._Current); + } + + // clang-format off + friend constexpr void iter_swap(const _Iterator& _Lhs, const _Iterator& _Rhs) + noexcept((noexcept(_RANGES iter_swap( + _STD declval>&>(), + _STD declval>&>())) && ...)) + requires (indirectly_swappable>> && ...) + { + // clang-format on + const auto _Swap_every_pair_closure = + [&_Lhs, &_Rhs ](index_sequence<_Indices...>) noexcept(noexcept( + ((_RANGES iter_swap(_STD get<_Indices>(_Lhs._Current), _STD get<_Indices>(_Rhs._Current))), + ...))) { + ((_RANGES iter_swap(_STD get<_Indices>(_Lhs._Current), _STD get<_Indices>(_Rhs._Current))), ...); + }; + + _Swap_every_pair_closure(index_sequence_for<_ViewTypes...>{}); + } + }; + + template + class _Sentinel { + private: + friend zip_view; + + using _My_tuple = tuple>...>; + + /* [[no_unique_address]] */ _My_tuple _End; + + constexpr explicit _Sentinel(_My_tuple _End_) noexcept( + is_nothrow_copy_constructible_v<_My_tuple>) // strengthened + : _End(_End_) {} + + template + _NODISCARD static constexpr const auto& _Get_iterator_tuple( + const _Iterator<_IteratorConst>& _Itr) noexcept { + // NOTE: This function is necessary because friend functions are never + // member functions, and friendship is not transitive. + return _Itr._Current; + } + + public: + _Sentinel() = default; + + constexpr _Sentinel(_Sentinel _Rhs) noexcept( + (is_nothrow_convertible_v, sentinel_t> && ...)) // strengthened + requires (_IsConst && (convertible_to, sentinel_t> && ...)) + : _End(_STD move(_Rhs._End)) {} + + template + requires (sentinel_for>, + iterator_t<_Maybe_const<_IteratorConst, _ViewTypes>>> + && ...) + _NODISCARD_FRIEND constexpr bool + operator==(const _Iterator<_IteratorConst>& _Lhs, const _Sentinel& _Rhs) noexcept( + noexcept(_Zip_iterator_sentinel_equal(_Get_iterator_tuple(_Lhs), _Rhs._End))) /* strengthened */ { + return _Zip_iterator_sentinel_equal(_Get_iterator_tuple(_Lhs), _Rhs._End); + } + + template + requires (sized_sentinel_for>, + iterator_t<_Maybe_const<_IteratorConst, _ViewTypes>>> + && ...) + _NODISCARD_FRIEND constexpr common_type_t>...> + operator-(const _Iterator<_IteratorConst>& _Lhs, const _Sentinel& _Rhs) noexcept( + noexcept(_Zip_get_smallest_distance< + common_type_t>...>>( + _Get_iterator_tuple(_Lhs), _Rhs._End))) /* strengthened */ { + using _Difference_type = common_type_t>...>; + return _Zip_get_smallest_distance<_Difference_type>(_Get_iterator_tuple(_Lhs), _Rhs._End); + } + + template + requires (sized_sentinel_for>, + iterator_t<_Maybe_const<_IteratorConst, _ViewTypes>>> + && ...) + _NODISCARD_FRIEND constexpr common_type_t>...> + operator-(const _Sentinel& _Lhs, const _Iterator<_IteratorConst>& _Rhs) noexcept( + noexcept(-(_Rhs - _Lhs))) /* strengthened */ { + return -(_Rhs - _Lhs); + } + }; + + template + static constexpr bool _Is_end_noexcept = []() { + if constexpr (!_Zip_is_common<_Maybe_const<_IsConst, _ViewTypes>...>) { + return noexcept(_Sentinel<_IsConst>{ + _Tuple_transform(_RANGES end, _STD declval<_Maybe_const<_IsConst, tuple<_ViewTypes...>>&>())}); + } else if constexpr ((random_access_range<_Maybe_const<_IsConst, _ViewTypes>> && ...)) { + // Operations on iter_difference_t values must be noexcept, so we only + // need to check the noexcept of begin(). + return noexcept(_STD declval<_Maybe_const<_IsConst, zip_view>&>().begin()); + } else { + return noexcept(_Iterator<_IsConst>{ + _Tuple_transform(_RANGES end, _STD declval<_Maybe_const<_IsConst, tuple<_ViewTypes...>>&>())}); + } + }(); + + public: + zip_view() noexcept((is_nothrow_default_constructible_v<_ViewTypes> && ...)) = default; + + constexpr explicit zip_view(_ViewTypes... _Args) noexcept( + (is_nothrow_move_constructible_v<_ViewTypes> && ...)) // strengthened + : _Views(_STD move(_Args)...) {} + + _NODISCARD constexpr auto begin() noexcept( + noexcept(_Iterator{_Tuple_transform(_RANGES begin, _Views)})) // strengthened + requires (!(_Simple_view<_ViewTypes> && ...)) + { + return _Iterator{_Tuple_transform(_RANGES begin, _Views)}; + } + + _NODISCARD constexpr auto begin() const + noexcept(noexcept(_Iterator{_Tuple_transform(_RANGES begin, _Views)})) // strengthened + requires (range && ...) + { + return _Iterator{_Tuple_transform(_RANGES begin, _Views)}; + } + + _NODISCARD constexpr auto end() noexcept(_Is_end_noexcept) // strengthened + requires (!(_Simple_view<_ViewTypes> && ...)) + { + if constexpr (!_Zip_is_common<_ViewTypes...>) { + return _Sentinel{_Tuple_transform(_RANGES end, _Views)}; + } else if constexpr ((random_access_range<_ViewTypes> && ...)) { + return begin() + static_cast>>(size()); + } else { + return _Iterator{_Tuple_transform(_RANGES end, _Views)}; + } + } + + _NODISCARD constexpr auto end() const noexcept(_Is_end_noexcept) // strengthened + requires (range && ...) + { + if constexpr (!_Zip_is_common) { + return _Sentinel{_Tuple_transform(_RANGES end, _Views)}; + } else if constexpr ((random_access_range && ...)) { + return begin() + static_cast>>(size()); + } else { + return _Iterator{_Tuple_transform(_RANGES end, _Views)}; + } + } + + _NODISCARD constexpr auto size() noexcept( + noexcept(_STD apply(_Size_closure(), _Tuple_transform(_RANGES size, _Views)))) // strengthened + requires (sized_range<_ViewTypes> && ...) + { + return _STD apply(_Size_closure(), _Tuple_transform(_RANGES size, _Views)); + } + + _NODISCARD constexpr auto size() const + noexcept(noexcept(_STD apply(_Size_closure(), _Tuple_transform(_RANGES size, _Views)))) // strengthened + requires (sized_range && ...) + { + return _STD apply(_Size_closure(), _Tuple_transform(_RANGES size, _Views)); + } + + private: + _NODISCARD static constexpr auto _Size_closure() noexcept { + return [](auto... _Sizes) noexcept { + using _Common_unsigned_type = _Make_unsigned_like_t>; + return (_RANGES min)({static_cast<_Common_unsigned_type>(_Sizes)...}); + }; + } + }; + + template + zip_view(_RangeTypes&&...) -> zip_view...>; + + template + inline constexpr bool enable_borrowed_range> = (enable_borrowed_range<_ViewTypes> && ...); + + namespace views { + struct _Zip_fn { + private: + template + _NODISCARD static _CONSTEVAL bool _Is_invocation_noexcept() { + if constexpr (sizeof...(_Types) == 0) { + // NOTE: views::empty> is nothrow copy-constructible. + return true; + } else { + return noexcept(zip_view...>(_STD declval<_Types>()...)); + } + } + + public: + template + _NODISCARD constexpr auto operator()(_Types&&... _Args) const noexcept(_Is_invocation_noexcept<_Types...>()) + requires (sizeof...(_Types) == 0) + || requires { zip_view...>(_STD forward<_Types>(_Args)...); } + { + if constexpr (sizeof...(_Types) == 0) { + return empty_view>{}; + } else { + return zip_view...>(_STD forward<_Types>(_Args)...); + } + } + }; + + _EXPORT_STD inline constexpr _Zip_fn zip{}; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/stl/inc/xutility b/stl/inc/xutility index ba8ed1e070b..7f0bad2dd08 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -78,6 +78,16 @@ const void* __stdcall __std_find_trivial_unsized_1(const void* _First, uint8_t _ const void* __stdcall __std_find_trivial_unsized_2(const void* _First, uint16_t _Val) noexcept; const void* __stdcall __std_find_trivial_unsized_4(const void* _First, uint32_t _Val) noexcept; const void* __stdcall __std_find_trivial_unsized_8(const void* _First, uint64_t _Val) noexcept; + +const void* __stdcall __std_min_element_1(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_min_element_2(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_min_element_4(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_min_element_8(const void* _First, const void* _Last, bool _Signed) noexcept; + +const void* __stdcall __std_max_element_1(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_max_element_2(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_max_element_4(const void* _First, const void* _Last, bool _Signed) noexcept; +const void* __stdcall __std_max_element_8(const void* _First, const void* _Last, bool _Signed) noexcept; _END_EXTERN_C template @@ -139,6 +149,40 @@ _Ty* __std_find_trivial_unsized(_Ty* _First, const _TVal _Val) noexcept { } } +template +_Ty* __std_min_element(_Ty* _First, _Ty* _Last) noexcept { + constexpr bool _Signed = _STD is_signed_v<_Ty>; + + if constexpr (sizeof(_Ty) == 1) { + return const_cast<_Ty*>(static_cast(__std_min_element_1(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 2) { + return const_cast<_Ty*>(static_cast(__std_min_element_2(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 4) { + return const_cast<_Ty*>(static_cast(__std_min_element_4(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 8) { + return const_cast<_Ty*>(static_cast(__std_min_element_8(_First, _Last, _Signed))); + } else { + static_assert(_STD _Always_false<_Ty>, "Unexpected size"); + } +} + +template +_Ty* __std_max_element(_Ty* _First, _Ty* _Last) noexcept { + constexpr bool _Signed = _STD is_signed_v<_Ty>; + + if constexpr (sizeof(_Ty) == 1) { + return const_cast<_Ty*>(static_cast(__std_max_element_1(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 2) { + return const_cast<_Ty*>(static_cast(__std_max_element_2(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 4) { + return const_cast<_Ty*>(static_cast(__std_max_element_4(_First, _Last, _Signed))); + } else if constexpr (sizeof(_Ty) == 8) { + return const_cast<_Ty*>(static_cast(__std_max_element_8(_First, _Last, _Signed))); + } else { + static_assert(_STD _Always_false<_Ty>, "Unexpected size"); + } +} + #endif // _USE_STD_VECTOR_ALGORITHMS _STD_BEGIN @@ -5918,6 +5962,401 @@ namespace ranges { } // namespace ranges #endif // __cpp_lib_concepts +template > +_INLINE_VAR constexpr bool _Is_min_max_optimization_safe = // Activate the vector algorithms for min_/max_element? + _Iterator_is_contiguous<_Iter> // The iterator must be contiguous so we can get raw pointers. + && !_Iterator_is_volatile<_Iter> // The iterator must not be volatile. + && conjunction_v, is_pointer<_Elem>>, // Element is of integral or pointer type. + disjunction< // And either of the following: +#ifdef __cpp_lib_concepts + is_same<_Pr, _RANGES less>, // predicate is ranges::less +#endif // __cpp_lib_concepts + is_same<_Pr, less<>>, is_same<_Pr, less<_Elem>>>>; // predicate is less + +template +constexpr _FwdIt _Max_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) { + if (!_Is_constant_evaluated()) { + const auto _First_ptr = _To_address(_First); + const auto _Result = __std_max_element(_First_ptr, _To_address(_Last)); + if constexpr (is_pointer_v<_FwdIt>) { + return _Result; + } else { + return _First + (_Result - _First_ptr); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + _FwdIt _Found = _First; + if (_First != _Last) { + while (++_First != _Last) { + if (_DEBUG_LT_PRED(_Pred, *_Found, *_First)) { + _Found = _First; + } + } + } + + return _Found; +} + +_EXPORT_STD template +_NODISCARD constexpr _FwdIt max_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element + _Adl_verify_range(_First, _Last); + _Seek_wrapped(_First, _Max_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred))); + return _First; +} + +_EXPORT_STD template +_NODISCARD constexpr _FwdIt max_element(_FwdIt _First, _FwdIt _Last) { // find largest element + return _STD max_element(_First, _Last, less<>{}); +} + +#if _HAS_CXX17 +_EXPORT_STD template = 0> +_NODISCARD _FwdIt max_element(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Pr _Pred) noexcept /* terminates */ { + // find largest element + // not parallelized at present, parallelism expected to be feasible in a future release + return _STD max_element(_First, _Last, _Pass_fn(_Pred)); +} + +_EXPORT_STD template = 0> +_NODISCARD _FwdIt max_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* terminates */ { + // find largest element + // not parallelized at present, parallelism expected to be feasible in a future release + return _STD max_element(_First, _Last); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + template + _NODISCARD constexpr _It _Max_element_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe<_It, _Pr> + && sized_sentinel_for<_Se, _It>) { + if (!_STD is_constant_evaluated()) { + const auto _First_ptr = _STD to_address(_First); + const auto _Last_ptr = _First_ptr + (_Last - _First); + const auto _Result = __std_max_element(_First_ptr, _Last_ptr); + if constexpr (is_pointer_v<_It>) { + return _Result; + } else { + return _First + (_Result - _First_ptr); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + auto _Found = _First; + if (_First == _Last) { + return _Found; + } + + while (++_First != _Last) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Found), _STD invoke(_Proj, *_First))) { + _Found = _First; + } + } + + return _Found; + } + + class _Max_element_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_strict_weak_order> _Pr = ranges::less> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + _Seek_wrapped(_First, _RANGES _Max_element_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj))); + return _First; + } + + template , _Pj>> _Pr = ranges::less> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + _Seek_wrapped(_First, _RANGES _Max_element_unchecked(_Unwrap_range_iter<_Rng>(_STD move(_First)), + _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj))); + return _First; + } + }; + + _EXPORT_STD inline constexpr _Max_element_fn max_element{_Not_quite_object::_Construct_tag {}}; +} // namespace ranges +#endif // __cpp_lib_concepts +#endif // _HAS_CXX17 + +_EXPORT_STD template +_NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist, _Pr _Pred) { + // return leftmost/largest + const _Ty* _Res = _Max_element_unchecked(_Ilist.begin(), _Ilist.end(), _Pass_fn(_Pred)); + return *_Res; +} + +_EXPORT_STD template +_NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist) { + // return leftmost/largest + return (_STD max)(_Ilist, less<>{}); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + template + concept _Prefer_iterator_copies = // When we have a choice, should we copy iterators or copy elements? + // pre: input_iterator<_It> + sizeof(_It) <= 2 * sizeof(iter_value_t<_It>) + && (is_trivially_copyable_v<_It> || !is_trivially_copyable_v>); + + class _Max_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template > _Pr = ranges::less> + _NODISCARD constexpr const _Ty& operator()( + const _Ty& _Left, const _Ty& _Right, _Pr _Pred = {}, _Pj _Proj = {}) const { + if (_STD invoke(_Pred, _STD invoke(_Proj, _Left), _STD invoke(_Proj, _Right))) { + return _Right; + } else { + return _Left; + } + } + + template > _Pr = ranges::less> + _NODISCARD constexpr _Ty operator()(initializer_list<_Ty> _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + const auto _First = _Range.begin(); + const auto _Last = _Range.end(); + _STL_ASSERT(_First != _Last, + "An initializer_list passed to std::ranges::max must not be empty. (N4861 [alg.min.max]/13)"); + return *_RANGES _Max_element_unchecked(_First, _Last, _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + + template , _Pj>> _Pr = ranges::less> + requires indirectly_copyable_storable, range_value_t<_Rng>*> + _NODISCARD constexpr range_value_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _UFirst = _Ubegin(_Range); + auto _ULast = _Uend(_Range); + _STL_ASSERT( + _UFirst != _ULast, "A range passed to std::ranges::max must not be empty. (N4861 [alg.min.max]/13)"); + if constexpr (forward_range<_Rng> && _Prefer_iterator_copies>) { + return static_cast>(*_RANGES _Max_element_unchecked( + _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj))); + } else { + range_value_t<_Rng> _Found(*_UFirst); + while (++_UFirst != _ULast) { + if (_STD invoke(_Pred, _STD invoke(_Proj, _Found), _STD invoke(_Proj, *_UFirst))) { + _Found = *_UFirst; + } + } + + return _Found; + } + } + }; + + _EXPORT_STD inline constexpr _Max_fn max{_Not_quite_object::_Construct_tag {}}; +} // namespace ranges +#endif // __cpp_lib_concepts + +template +constexpr _FwdIt _Min_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find smallest element +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) { + if (!_Is_constant_evaluated()) { + const auto _First_ptr = _To_address(_First); + const auto _Result = __std_min_element(_First_ptr, _To_address(_Last)); + if constexpr (is_pointer_v<_FwdIt>) { + return _Result; + } else { + return _First + (_Result - _First_ptr); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + _FwdIt _Found = _First; + if (_First != _Last) { + while (++_First != _Last) { + if (_DEBUG_LT_PRED(_Pred, *_First, *_Found)) { + _Found = _First; + } + } + } + + return _Found; +} + +_EXPORT_STD template +_NODISCARD constexpr _FwdIt min_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find smallest element + _Adl_verify_range(_First, _Last); + _Seek_wrapped(_First, _Min_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred))); + return _First; +} + +_EXPORT_STD template +_NODISCARD constexpr _FwdIt min_element(_FwdIt _First, _FwdIt _Last) { // find smallest element + return _STD min_element(_First, _Last, less<>{}); +} + +#if _HAS_CXX17 +_EXPORT_STD template = 0> +_NODISCARD _FwdIt min_element(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Pr _Pred) noexcept /* terminates */ { + // find smallest element + // not parallelized at present, parallelism expected to be feasible in a future release + return _STD min_element(_First, _Last, _Pass_fn(_Pred)); +} + +_EXPORT_STD template = 0> +_NODISCARD _FwdIt min_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* terminates */ { + // find smallest element + // not parallelized at present, parallelism expected to be feasible in a future release + return _STD min_element(_First, _Last); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + template + _NODISCARD constexpr _It _Min_element_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe<_It, _Pr> + && sized_sentinel_for<_Se, _It>) { + if (!_STD is_constant_evaluated()) { + const auto _First_ptr = _STD to_address(_First); + const auto _Last_ptr = _First_ptr + (_Last - _First); + const auto _Result = __std_min_element(_First_ptr, _Last_ptr); + if constexpr (is_pointer_v<_It>) { + return _Result; + } else { + return _First + (_Result - _First_ptr); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + auto _Found = _First; + if (_First == _Last) { + return _Found; + } + + while (++_First != _Last) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Found))) { + _Found = _First; + } + } + + return _Found; + } + + class _Min_element_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_strict_weak_order> _Pr = ranges::less> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + _Seek_wrapped(_First, _RANGES _Min_element_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj))); + return _First; + } + + template , _Pj>> _Pr = ranges::less> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + _Seek_wrapped(_First, _RANGES _Min_element_unchecked(_Unwrap_range_iter<_Rng>(_STD move(_First)), + _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj))); + return _First; + } + }; + + _EXPORT_STD inline constexpr _Min_element_fn min_element{_Not_quite_object::_Construct_tag {}}; +} // namespace ranges +#endif // __cpp_lib_concepts +#endif // _HAS_CXX17 + +_EXPORT_STD template +_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist, _Pr _Pred) { + // return leftmost/smallest + const _Ty* _Res = _Min_element_unchecked(_Ilist.begin(), _Ilist.end(), _Pass_fn(_Pred)); + return *_Res; +} + +_EXPORT_STD template +_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist) { + // return leftmost/smallest + return (_STD min)(_Ilist, less<>{}); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + class _Min_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template > _Pr = ranges::less> + _NODISCARD constexpr const _Ty& operator()( + const _Ty& _Left, const _Ty& _Right, _Pr _Pred = {}, _Pj _Proj = {}) const { + if (_STD invoke(_Pred, _STD invoke(_Proj, _Right), _STD invoke(_Proj, _Left))) { + return _Right; + } else { + return _Left; + } + } + + template > _Pr = ranges::less> + _NODISCARD constexpr _Ty operator()(initializer_list<_Ty> _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + const auto _First = _Range.begin(); + const auto _Last = _Range.end(); + _STL_ASSERT(_First != _Last, + "An initializer_list passed to std::ranges::min must not be empty. (N4861 [alg.min.max]/5)"); + return *_RANGES _Min_element_unchecked(_First, _Last, _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + + template , _Pj>> _Pr = ranges::less> + requires indirectly_copyable_storable, range_value_t<_Rng>*> + _NODISCARD constexpr range_value_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _UFirst = _Ubegin(_Range); + auto _ULast = _Uend(_Range); + _STL_ASSERT( + _UFirst != _ULast, "A range passed to std::ranges::min must not be empty. (N4861 [alg.min.max]/5)"); + if constexpr (forward_range<_Rng> && _Prefer_iterator_copies>) { + return static_cast>(*_RANGES _Min_element_unchecked( + _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj))); + } else { + range_value_t<_Rng> _Found(*_UFirst); + while (++_UFirst != _ULast) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, _Found))) { + _Found = *_UFirst; + } + } + + return _Found; + } + } + }; + + _EXPORT_STD inline constexpr _Min_fn min{_Not_quite_object::_Construct_tag {}}; +} // namespace ranges +#endif // __cpp_lib_concepts + _EXPORT_STD template _NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val, _Pr _Pred) { // find first element not before _Val diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 7e7c78319ff..c78dae8351c 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -319,13 +319,15 @@ // P1989R2 Range Constructor For string_view // P2077R3 Heterogeneous Erasure Overloads For Associative Containers // P2136R3 invoke_r() +// P2165R4 Compatibility Between tuple, pair, And tuple-like Objects +// (changes to views::zip only) // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip -// (changes to pair, tuple, and vector::reference only) +// (missing views::zip_transform, views::adjacent, and views::adjacent_transform) // P2387R3 Pipe Support For User-Defined Range Adaptors // P2417R2 More constexpr bitset // P2438R2 string::substr() && diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 1182ac9f0bb..d57279814cb 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -43,6 +43,9 @@ std/containers/unord/unord.set/insert_and_emplace_allocator_requirements.pass.cp # Bogus test believes that copyability of array must be the same as array std/containers/sequences/array/array.cons/implicit_copy.pass.cpp FAIL +# Bogus test returns a non-const int& using a const iterator's member variable +std/ranges/range.adaptors/range.zip/end.pass.cpp FAIL + # libc++ doesn't implement LWG-3648: "format should not print bool with 'c'" std/utilities/format/format.functions/vformat_to.locale.pass.cpp FAIL std/utilities/format/format.functions/vformat_to.pass.cpp FAIL @@ -55,6 +58,21 @@ std/strings/basic.string/string.nonmembers/string_op+/allocator_propagation.pass # libc++ hasn't updated move_iterator for P2520R0 std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp FAIL +# libc++ doesn't implement LWG-3692: "zip_view::iterator's operator<=> is overconstrained" +std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp FAIL + +# libc++ is incorrect on the constraints for constructors of zip_view::iterator and zip_view::sentinel +std/ranges/range.adaptors/range.zip/begin.pass.cpp SKIPPED +std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp SKIPPED + +# libc++ has not implemented P2165R4: "Compatibility between tuple, pair, and tuple-like objects" +# for zip_view +std/ranges/range.adaptors/range.zip/cpo.pass.cpp FAIL +std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp FAIL +std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp FAIL +std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp FAIL +std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp FAIL + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" @@ -192,32 +210,6 @@ std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp SKIPPED # P2321R2 zip std/language.support/support.limits/support.limits.general/tuple.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/begin.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/cpo.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/end.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/general.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp FAIL -std/ranges/range.adaptors/range.zip/size.pass.cpp FAIL # P2255R2 "Type Traits To Detect References Binding To Temporaries" std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 66038b777dc..8b1bf56d76b 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -43,6 +43,9 @@ containers\unord\unord.set\insert_and_emplace_allocator_requirements.pass.cpp # Bogus test believes that copyability of array must be the same as array containers\sequences\array\array.cons\implicit_copy.pass.cpp +# Bogus test returns a non-const int& using a const iterator's member variable +ranges\range.adaptors\range.zip\end.pass.cpp + # libc++ doesn't implement LWG-3648: "format should not print bool with 'c'" utilities\format\format.functions\vformat_to.locale.pass.cpp utilities\format\format.functions\vformat_to.pass.cpp @@ -55,6 +58,21 @@ strings\basic.string\string.nonmembers\string_op+\allocator_propagation.pass.cpp # libc++ hasn't updated move_iterator for P2520R0 iterators\predef.iterators\move.iterators\move.iterator\types.pass.cpp +# libc++ doesn't implement LWG-3692: "zip_view::iterator's operator<=> is overconstrained" +ranges\range.adaptors\range.zip\iterator\compare.pass.cpp + +# libc++ is incorrect on the constraints for constructors of zip_view::iterator and zip_view::sentinel +ranges\range.adaptors\range.zip\begin.pass.cpp +ranges\range.adaptors\range.zip\sentinel\ctor.default.pass.cpp + +# libc++ has not implemented P2165R4: "Compatibility between tuple, pair, and tuple-like objects" +# for zip_view +ranges\range.adaptors\range.zip\cpo.pass.cpp +ranges\range.adaptors\range.zip\ctor.default.pass.cpp +ranges\range.adaptors\range.zip\iterator\deref.pass.cpp +ranges\range.adaptors\range.zip\iterator\member_types.compile.pass.cpp +ranges\range.adaptors\range.zip\iterator\subscript.pass.cpp + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" @@ -192,32 +210,6 @@ utilities\tuple\tuple.tuple\tuple.cnstr\recursion_depth.pass.cpp # P2321R2 zip language.support\support.limits\support.limits.general\tuple.version.compile.pass.cpp language.support\support.limits\support.limits.general\utility.version.compile.pass.cpp -ranges\range.adaptors\range.zip\begin.pass.cpp -ranges\range.adaptors\range.zip\borrowing.compile.pass.cpp -ranges\range.adaptors\range.zip\cpo.pass.cpp -ranges\range.adaptors\range.zip\ctad.compile.pass.cpp -ranges\range.adaptors\range.zip\ctor.default.pass.cpp -ranges\range.adaptors\range.zip\ctor.views.pass.cpp -ranges\range.adaptors\range.zip\end.pass.cpp -ranges\range.adaptors\range.zip\general.pass.cpp -ranges\range.adaptors\range.zip\iterator\arithmetic.pass.cpp -ranges\range.adaptors\range.zip\iterator\compare.pass.cpp -ranges\range.adaptors\range.zip\iterator\ctor.default.pass.cpp -ranges\range.adaptors\range.zip\iterator\ctor.other.pass.cpp -ranges\range.adaptors\range.zip\iterator\decrement.pass.cpp -ranges\range.adaptors\range.zip\iterator\deref.pass.cpp -ranges\range.adaptors\range.zip\iterator\increment.pass.cpp -ranges\range.adaptors\range.zip\iterator\iter_move.pass.cpp -ranges\range.adaptors\range.zip\iterator\iter_swap.pass.cpp -ranges\range.adaptors\range.zip\iterator\member_types.compile.pass.cpp -ranges\range.adaptors\range.zip\iterator\singular.pass.cpp -ranges\range.adaptors\range.zip\iterator\subscript.pass.cpp -ranges\range.adaptors\range.zip\range.concept.compile.pass.cpp -ranges\range.adaptors\range.zip\sentinel\ctor.default.pass.cpp -ranges\range.adaptors\range.zip\sentinel\ctor.other.pass.cpp -ranges\range.adaptors\range.zip\sentinel\eq.pass.cpp -ranges\range.adaptors\range.zip\sentinel\minus.pass.cpp -ranges\range.adaptors\range.zip\size.pass.cpp # P2255R2 "Type Traits To Detect References Binding To Temporaries" language.support\support.limits\support.limits.general\type_traits.version.compile.pass.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 24d9ded681f..fe5a3ff0945 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -548,6 +548,7 @@ tests\P2273R3_constexpr_unique_ptr tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference +tests\P2321R2_views_zip tests\P2387R3_bind_back tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange diff --git a/tests/std/tests/P2321R2_views_zip/env.lst b/tests/std/tests/P2321R2_views_zip/env.lst new file mode 100644 index 00000000000..6e5f62c042f --- /dev/null +++ b/tests/std/tests/P2321R2_views_zip/env.lst @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# NOTE: zip_view::_Iterator defines ADL overloads for iter_swap and iter_move, +# so the test fails to compile unless /permissive- is set. This issue isn't +# unique to zip_view; all view types whose iterators define overloads for these +# functions have this requirement. +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst +RUNALL_CROSSLIST +PM_CL="/DTEST_INPUT" +PM_CL="/DTEST_FORWARD" +PM_CL="/DTEST_BIDIRECTIONAL" +PM_CL="/DTEST_RANDOM" diff --git a/tests/std/tests/P2321R2_views_zip/test.cpp b/tests/std/tests/P2321R2_views_zip/test.cpp new file mode 100644 index 00000000000..5609ca057db --- /dev/null +++ b/tests/std/tests/P2321R2_views_zip/test.cpp @@ -0,0 +1,887 @@ +// 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 +concept CanViewZip = requires(RangeTypes&&... ranges) { views::zip(std::forward(ranges)...); }; + +template +using AllView = views::all_t; + +template +concept CanTestElementType = // Required for iter_swap test + (copy_constructible && equality_comparable && swappable); + +template + requires (CanTestElementType && CanTestElementType && CanTestElementType) +class three_element_test_container { +private: + array type_one_array; + array type_two_array; + array type_three_array; + +public: + using element_tuple_type = tuple; + using const_element_tuple_type = tuple; + + using reference_tuple_type = tuple; + using const_reference_tuple_type = tuple; + using rvalue_reference_tuple_type = tuple; + using const_rvalue_reference_tuple_type = tuple; + + static constexpr size_t smallest_array_size = (min) ({Type1Size, Type2Size, Type3Size}); + + constexpr three_element_test_container(const array& type_one_init_arr, + const array& type_two_init_arr, const array& type_three_init_arr) + : type_one_array(type_one_init_arr), type_two_array(type_two_init_arr), type_three_array(type_three_init_arr) {} + + template + constexpr auto get_element_span() { + STATIC_ASSERT(ElementIndex < 3); + + if constexpr (ElementIndex == 0) { + return span{type_one_array}; + } else if constexpr (ElementIndex == 1) { + return span{type_two_array}; + } else { + return span{type_three_array}; + } + } + + template + constexpr auto get_element_span() const { + STATIC_ASSERT(ElementIndex < 3); + + if constexpr (ElementIndex == 0) { + return span, Type1Size>{type_one_array}; + } else if constexpr (ElementIndex == 1) { + return span, Type2Size>{type_two_array}; + } else { + return span, Type3Size>{type_three_array}; + } + } + + template + constexpr auto& get_underlying_element_array() { + STATIC_ASSERT(ElementIndex < 3); + + if constexpr (ElementIndex == 0) { + return type_one_array; + } else if constexpr (ElementIndex == 1) { + return type_two_array; + } else { + return type_three_array; + } + } + + template + constexpr const auto& get_underlying_element_array() const { + STATIC_ASSERT(ElementIndex < 3); + + if constexpr (ElementIndex == 0) { + return type_one_array; + } else if constexpr (ElementIndex == 1) { + return type_two_array; + } else { + return type_three_array; + } + } + + constexpr reference_tuple_type get_expected_element_tuple(const size_t index) { + assert(index < smallest_array_size); + return reference_tuple_type{type_one_array[index], type_two_array[index], type_three_array[index]}; + } + + constexpr const_reference_tuple_type get_expected_element_tuple(const size_t index) const { + assert(index < smallest_array_size); + return const_reference_tuple_type{type_one_array[index], type_two_array[index], type_three_array[index]}; + } + + constexpr array get_element_tuple_arr() { + const auto make_tuple_arr_lambda = [this](index_sequence) { + return array{get_expected_element_tuple(Indices)...}; + }; + + return make_tuple_arr_lambda(make_index_sequence{}); + } + + constexpr array get_element_tuple_arr() const { + const auto make_tuple_arr_lambda = [this](index_sequence) { + return array{get_expected_element_tuple(Indices)...}; + }; + + return make_tuple_arr_lambda(make_index_sequence{}); + } +}; + +template + requires (CanTestElementType) +class single_element_test_container { +private: + array element_array; + +public: + using element_tuple_type = tuple; + using const_element_tuple_type = tuple; + + using reference_tuple_type = tuple; + using const_reference_tuple_type = tuple; + using rvalue_reference_tuple_type = tuple; + using const_rvalue_reference_tuple_type = tuple; + + static constexpr size_t smallest_array_size = Size; + + constexpr single_element_test_container(const array& element_init_arr) + : element_array(element_init_arr) {} + + constexpr auto get_element_span() { + return span{element_array}; + } + + constexpr auto get_element_span() const { + return span>{element_array}; + } + + constexpr auto& get_underlying_element_array() { + return element_array; + } + + constexpr const auto& get_underlying_element_array() const { + return element_array; + } + + constexpr reference_tuple_type get_expected_element_tuple(const size_t index) { + assert(index < smallest_array_size); + return reference_tuple_type{element_array[index]}; + } + + constexpr const_reference_tuple_type get_expected_element_tuple(const size_t index) const { + assert(index < smallest_array_size); + return const_reference_tuple_type{element_array[index]}; + } + + constexpr array get_element_tuple_arr() { + const auto make_tuple_arr_lambda = [this](index_sequence) { + return array{get_expected_element_tuple(Indices)...}; + }; + + return make_tuple_arr_lambda(make_index_sequence{}); + } + + constexpr array get_element_tuple_arr() const { + const auto make_tuple_arr_lambda = [this](index_sequence) { + return array{get_expected_element_tuple(Indices)...}; + }; + + return make_tuple_arr_lambda(make_index_sequence{}); + } +}; + +// NOTE: views::zip shouldn't care about whether or not the views use proxy references, +// but for our tests, we need to. +template +struct reference_type_solver { + using reference_type = const T&; +}; + +template +struct reference_type_solver> { + using reference_type = const Element&; +}; + +template +constexpr bool do_tuples_reference_same_objects(const LHSTupleType& lhs_tuple, const RHSTupleType& rhs_tuple) { + STATIC_ASSERT(tuple_size_v == tuple_size_v); + + const auto evaluate_single_element_lambda = [&lhs_tuple, &rhs_tuple]() { + using reference_type = typename reference_type_solver>::reference_type; + return addressof(static_cast(get(lhs_tuple))) + == addressof(static_cast(get(rhs_tuple))); + }; + + using index_sequence_type = make_index_sequence>; + const auto evaluate_tuples_lambda = + [&evaluate_single_element_lambda](index_sequence) { + return (evaluate_single_element_lambda.template operator()() && ...); + }; + + return evaluate_tuples_lambda(index_sequence_type{}); +} + +#pragma warning(push) +#pragma warning(disable : 4100) // unreferenced formal parameter + +template +constexpr bool test_one(TestContainerType& test_container, RangeTypes&&... ranges) { + // Ignore instances where one of the generated test ranges does not model + // ranges::viewable_range. + if constexpr ((ranges::viewable_range && ...)) { + using ZipType = ranges::zip_view...>; + + constexpr bool are_views = (ranges::view> && ...) && (sizeof...(RangeTypes) > 0); + + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(ranges::forward_range == (ranges::forward_range && ...)); + STATIC_ASSERT(ranges::bidirectional_range == (ranges::bidirectional_range && ...)); + STATIC_ASSERT(ranges::random_access_range == (ranges::random_access_range && ...)); + STATIC_ASSERT( + ranges::common_range == (sizeof...(RangeTypes) == 1 && (ranges::common_range && ...)) + || (!(ranges::bidirectional_range && ...) && (ranges::common_range && ...)) + || ((ranges::random_access_range && ...) && (ranges::sized_range && ...))); + + // Validate conditional default-initializability + STATIC_ASSERT(is_default_constructible_v == (is_default_constructible_v> && ...)); + + // Validate conditional borrowed_range + STATIC_ASSERT(ranges::borrowed_range == (ranges::borrowed_range> && ...)); + + // Validate range adaptor object + + // ... with lvalue arguments + STATIC_ASSERT(CanViewZip == (!are_views || (copy_constructible> && ...))); + if constexpr (CanViewZip) { + using ExpectedZipType = ZipType; + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v> && ...); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::zip(ranges...)) == is_noexcept); + } + + // ... with const lvalue arguments + STATIC_ASSERT(CanViewZip&...> + == (!are_views || (copy_constructible> && ...))); + if constexpr (CanViewZip&...>) { + using ExpectedZipType = ranges::zip_view&>...>; + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v> && ...); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::zip(as_const(ranges)...)) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewZip...> + == (are_views || (movable> && ...))); + if constexpr (CanViewZip...>) { + using ExpectedZipType = ranges::zip_view>...>; + constexpr bool is_noexcept = (is_nothrow_move_constructible_v> && ...); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::zip(std::move(ranges)...)) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewZip...> + == (are_views && (copy_constructible> && ...))); + if constexpr (CanViewZip...>) { + using ExpectedZipType = ranges::zip_view>...>; + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v> && ...); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::zip(std::move(as_const(ranges))...)) == is_noexcept); + } + + // Validate deduction guide + same_as auto zipped_range = ranges::zip_view{std::forward(ranges)...}; + const auto tuple_element_arr = test_container.get_element_tuple_arr(); + const auto const_tuple_element_arr = as_const(test_container).get_element_tuple_arr(); + + // Validate zip_view::size() +#if defined(_MSVC_INTERNAL_TESTING) && !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-1655459 + STATIC_ASSERT(CanMemberSize == (ranges::sized_range> && ...)); + if constexpr (CanMemberSize) { + auto zip_size = zipped_range.size(); + + assert(zip_size == ranges::size(tuple_element_arr)); + } + + STATIC_ASSERT(CanMemberSize == (ranges::sized_range> && ...)); + if constexpr (CanMemberSize) { + auto zip_size = as_const(zipped_range).size(); + + assert(zip_size == ranges::size(tuple_element_arr)); + } +#else // ^^^ workaround / no workaround vvv + STATIC_ASSERT(CanMemberSize == (ranges::sized_range> && ...)); + if constexpr (CanMemberSize) { + using expected_size_type = + _Make_unsigned_like_t>()))...>>; + same_as auto zip_size = zipped_range.size(); + + assert(zip_size == ranges::size(tuple_element_arr)); + STATIC_ASSERT(noexcept(zipped_range.size()) + == (noexcept(static_cast(declval>().size())) && ...)); + } + + STATIC_ASSERT(CanMemberSize == (ranges::sized_range> && ...)); + if constexpr (CanMemberSize) { + using expected_size_type = + _Make_unsigned_like_t>()))...>>; + same_as auto zip_size = as_const(zipped_range).size(); + + assert(zip_size == ranges::size(tuple_element_arr)); + STATIC_ASSERT( + noexcept(as_const(zipped_range).size()) + == (noexcept(static_cast(declval>().size())) && ...)); + } +#endif // ^^^ no workaround ^^^ + + const bool is_empty = ranges::empty(tuple_element_arr); + + // Validate view_interface::empty() and view_interface::operator bool() + // + // From here on out, we'll be re-using concepts which we already verified to reduce + // redundancy. + STATIC_ASSERT(CanMemberEmpty == (ranges::sized_range || ranges::forward_range) ); + if constexpr (CanMemberEmpty) { + assert(zipped_range.empty() == is_empty); + } + + STATIC_ASSERT(CanMemberEmpty + == (ranges::sized_range || ranges::forward_range) ); + if constexpr (CanMemberEmpty) { + assert(as_const(zipped_range).empty() == is_empty); + } + + STATIC_ASSERT(CanBool == CanMemberEmpty); + if constexpr (CanBool) { + assert(static_cast(zipped_range) != is_empty); + } + + STATIC_ASSERT(CanBool == CanMemberEmpty); + if constexpr (CanBool) { + assert(static_cast(as_const(zipped_range)) != is_empty); + } + + // Validate contents of zipped range + assert(ranges::equal(zipped_range, tuple_element_arr, [](const auto& lhs_tuple, const auto& rhs_tuple) { + return do_tuples_reference_same_objects(lhs_tuple, rhs_tuple); + })); + +#pragma warning(push) +#pragma warning(disable : 4127) // Conditional Expression is Constant + if (!(ranges::forward_range> && ...)) { // intentionally not if constexpr + return true; + } +#pragma warning(pop) + + // Validate view_interface::data() + // + // This should never exist because zip_view's iterator never models contiguous_iterator. + STATIC_ASSERT(!contiguous_iterator>); + STATIC_ASSERT(!CanMemberData); + STATIC_ASSERT(!contiguous_iterator>); + STATIC_ASSERT(!CanMemberData); + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == ranges::random_access_range); + if constexpr (CanIndex) { + assert(do_tuples_reference_same_objects(zipped_range[0], tuple_element_arr[0])); + } + + STATIC_ASSERT(CanIndex == ranges::random_access_range); + if constexpr (CanIndex) { + assert(do_tuples_reference_same_objects(as_const(zipped_range)[0], tuple_element_arr[0])); + } + + // Validate view_interface::front() + STATIC_ASSERT(CanMemberFront == ranges::forward_range); + if constexpr (CanMemberFront) { + assert(do_tuples_reference_same_objects(zipped_range.front(), tuple_element_arr[0])); + } + + STATIC_ASSERT(CanMemberFront == ranges::forward_range); + if constexpr (CanMemberFront) { + assert(do_tuples_reference_same_objects(as_const(zipped_range).front(), const_tuple_element_arr[0])); + } + + // Validate view_interface::back() + STATIC_ASSERT( + CanMemberBack == (ranges::bidirectional_range && ranges::common_range) ); + if constexpr (CanMemberBack) { + assert(do_tuples_reference_same_objects( + zipped_range.back(), tuple_element_arr[TestContainerType::smallest_array_size - 1])); + } + + STATIC_ASSERT(CanMemberBack + == (ranges::bidirectional_range && ranges::common_range) ); + if constexpr (CanMemberBack) { + assert(do_tuples_reference_same_objects( + as_const(zipped_range).back(), const_tuple_element_arr[TestContainerType::smallest_array_size - 1])); + } + + // Validate zip_view::begin() + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto itr = zipped_range.begin(); + assert(do_tuples_reference_same_objects(*itr, tuple_element_arr[0])); + } + + STATIC_ASSERT(CanMemberBegin == (ranges::range> && ...)); + if constexpr (CanMemberBegin) { + assert(do_tuples_reference_same_objects(*(as_const(zipped_range).begin()), const_tuple_element_arr[0])); + } + + // Validate zip_view::end() + STATIC_ASSERT(CanMemberEnd); + if constexpr (equality_comparable>) { + auto end = zipped_range.begin(); + ranges::advance(end, TestContainerType::smallest_array_size); + + assert(end == zipped_range.end()); + } + + STATIC_ASSERT(CanMemberEnd == (ranges::range> && ...)); + if constexpr (CanMemberEnd && equality_comparable>) { + auto end = as_const(zipped_range).begin(); + ranges::advance(end, TestContainerType::smallest_array_size); + + assert(end == as_const(zipped_range).end()); + } + + const auto validate_iterators_lambda = []( + LocalZipType& relevant_range, + const ArrayType& relevant_tuple_element_arr) { + constexpr bool is_const = same_as>; + + STATIC_ASSERT(is_default_constructible_v> + == (is_default_constructible_v> && ...)); + + same_as> auto itr = relevant_range.begin(); + + // Validate iterator operator overloads + if constexpr (ranges::forward_range) { + assert(do_tuples_reference_same_objects(*itr++, relevant_tuple_element_arr[0])); + } else { + // If LocalZipType does not model forward_range, we can still use the postfix operator + // which returns void. + itr++; + } + + assert(do_tuples_reference_same_objects(*++itr, relevant_tuple_element_arr[2])); + + if constexpr (ranges::bidirectional_range) { + assert(do_tuples_reference_same_objects(*itr--, relevant_tuple_element_arr[2])); + assert(do_tuples_reference_same_objects(*--itr, relevant_tuple_element_arr[0])); + } + + if constexpr (ranges::random_access_range) { + itr += 2; + assert(do_tuples_reference_same_objects(*itr, relevant_tuple_element_arr[2])); + + itr -= 2; + assert(do_tuples_reference_same_objects(*itr, relevant_tuple_element_arr[0])); + + assert(do_tuples_reference_same_objects(itr[2], relevant_tuple_element_arr[2])); + + const same_as> auto itr2 = (itr + 2); + assert(do_tuples_reference_same_objects(*itr2, relevant_tuple_element_arr[2])); + + const same_as> auto itr3 = (2 + itr); + assert(do_tuples_reference_same_objects(*itr3, relevant_tuple_element_arr[2])); + + const same_as> auto itr4 = itr3 - 2; + assert(do_tuples_reference_same_objects(*itr4, relevant_tuple_element_arr[0])); + + using iterator_difference_type = common_type_t...>; + + const same_as auto diff1 = (itr2 - itr); + assert(diff1 == static_cast(2)); + + const same_as auto diff2 = (itr - itr2); + assert(diff2 == static_cast(-2)); + + if constexpr ((equality_comparable> && ...)) { + assert(itr == itr4); + assert(itr != itr2); + } + + // Per LWG-3692: The only constraint placed on operator<=> for zip_view's iterator is that + // (ranges::random_access_range) is satisfied. We need no additional checks. + using three_way_ordering_category = decltype(itr <=> itr2); + + assert(itr <=> itr4 == three_way_ordering_category::equivalent); + assert(itr <=> itr2 == three_way_ordering_category::less); + assert(itr2 <=> itr == three_way_ordering_category::greater); + } + + // Validate [ADL::]iter_move() + if constexpr (is_const) { + STATIC_ASSERT(is_same_v>>()))...>>); + } else { + STATIC_ASSERT(is_same_v>>()))...>>); + } + + STATIC_ASSERT( + noexcept(ranges::iter_move(itr)) + == (noexcept(ranges::iter_move(declval&>())) && ...) + && (is_nothrow_move_constructible_v> && ...)); + + assert(do_tuples_reference_same_objects(*itr, ranges::iter_move(itr))); + + // Validate [ADL::]iter_swap() + if constexpr ((indirectly_swappable> && ...)) { + const typename TestContainerType::element_tuple_type old_itr_value = *itr; + + // itr is currently at relevant_range.begin() right now, so we assign itr2 to that + // instead of itr. This avoids copy construction, which is problematic for move-only + // ranges. + same_as> auto itr2 = relevant_range.begin(); + itr2++; + + const typename TestContainerType::element_tuple_type old_itr2_value = *itr2; + + ranges::iter_swap(itr, itr2); + + assert(*itr == old_itr2_value); + assert(*itr2 == old_itr_value); + + // We can't test iter_swap()'s noexcept specifier without access to the member variables + // of the iterator. + } + + // Validate sentinels + if constexpr (!ranges::common_range) { + STATIC_ASSERT(is_default_constructible_v> + == (is_default_constructible_v> && ...)); + + const same_as> auto itr2 = relevant_range.begin(); + const same_as> auto sen = relevant_range.end(); + + if constexpr ((sentinel_for, ranges::iterator_t> + && ...)) { + auto advanced_itr = relevant_range.begin(); + ranges::advance(advanced_itr, TestContainerType::smallest_array_size); + + assert(advanced_itr == sen); + assert(itr2 != sen); + } + + if constexpr ((sized_sentinel_for, + ranges::iterator_t> + && ...)) { + using difference_type = common_type_t...>; + + const same_as auto diff1 = (itr2 - sen); + const same_as auto diff2 = (sen - itr2); + + assert(diff1 == -(static_cast(TestContainerType::smallest_array_size))); + assert(diff2 == static_cast(TestContainerType::smallest_array_size)); + } + } + }; + + // Validate iterators and sentinels + if constexpr (CanMemberBegin && CanMemberEnd) { + validate_iterators_lambda + .template operator()...>( + zipped_range, tuple_element_arr); + } + + if constexpr (CanMemberBegin && CanMemberEnd) { + validate_iterators_lambda + .template operator()...>( + as_const(zipped_range), const_tuple_element_arr); + } + } + + return true; +} + +#pragma warning(pop) + +// zip_view is sensitive to the following: +// +// - The categories of RangeTypes... +// - The commonality of RangeTypes... +// - Whether or not the size() member exists for each of RangeTypes..., as it is used by ranges::size() +// - Whether or not each range's iterator and sentinel models sized_sentinel_for +// - The sizeof...(RangeTypes) +// +// Other conditions are irrelevant. + +// TRANSITION, VSO-1655299: use a helper function as a workaround +template +_NODISCARD constexpr test::CanCompare test_range_can_compare() { + return test::CanCompare{to_bool(IsCommon) || derived_from}; +} + +template +using test_range = + test::range()>; + +constexpr array default_int_array{0, 1, 2, 3, 4, 5, 6}; + +constexpr single_element_test_container single_element_container_instance{default_int_array}; +constexpr three_element_test_container three_element_container_instance{ + default_int_array, array{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}, array{'a', 'b', 'c', 'd', 'e'}}; + +// The typical instantiator struct isn't templated like this, so that it could then be used with the +// existing testing machinery defined in range_algorithm_support.hpp. We can't use that stuff here, +// however, because views::zip takes multiple template parameters. +// +// The idea here might be useful for other view types which take multiple range parameters. Feel +// free to move something similar into range_algorithm_support.hpp if you feel that this could be +// used in another test suite. + +template +class range_type_solver { +protected: + template + using range_type = test::range(), test::ProxyRef{!derived_from}, + test::CanView::yes, test::Copyability::move_only>; +}; + +template <> +class range_type_solver { +protected: + template + using range_type = test_range; +}; + +template +class instantiator_impl : private range_type_solver { +private: + template + using range_type = typename range_type_solver::template range_type; + + template + struct first_type_solver {}; + + template + struct first_type_solver { + using first_type = FirstType; + }; + + using standard_range_tuple_type = tuple, + range_type, range_type>; + + template + struct differing_category_solver { + using category_type = input_iterator_tag; + }; + + template <> + struct differing_category_solver { + using category_type = forward_iterator_tag; + }; + + using differing_category_range_type = + range_type::category_type, int, IsSized, IsCommon, Diff>; + using differing_size_member_range_type = + range_type; + using differing_is_common_range_type = range_type; + using differing_iterator_sentinel_diff_range_type = range_type; + + template + static constexpr void test_single_range(ContainerType&& container) { + // Create a copy of the container. That way, we can always test iter_swap, + // even if container has const elements. + auto writable_single_element_container = container; + auto single_range = + tuple_element_t<0, standard_range_tuple_type>{writable_single_element_container.get_element_span()}; + + test_one(writable_single_element_container, single_range); + } + + template + static constexpr void test_three_ranges(ContainerType&& container) { + // Create a copy of the container. That way, we can always test iter_swap, + // even if container has const elements. + auto writable_three_element_container = container; + auto first_range = DifferingRangeType{writable_three_element_container.template get_element_span<0>()}; + auto second_range = tuple_element_t<1, standard_range_tuple_type>{ + writable_three_element_container.template get_element_span<1>()}; + auto third_range = tuple_element_t<2, standard_range_tuple_type>{ + writable_three_element_container.template get_element_span<2>()}; + + test_one(writable_three_element_container, first_range, second_range, third_range); + } + +public: + static constexpr void call() { + // Test the single-range use of views::zip (i.e., sizeof...(RangeTypes) == 1). + test_single_range(single_element_container_instance); + + // Test three ranges with views::zip with... + + // all of their traits being the same,... + test_three_ranges>(three_element_container_instance); + + // one range having a different category,... + test_three_ranges(three_element_container_instance); + + // one range having a different path for ranges::size(),... + test_three_ranges(three_element_container_instance); + + // one range having a different commonality,... + test_three_ranges(three_element_container_instance); + + // and one range having iterators and sentinels which model sized_sentinel_for + // differently. + test_three_ranges(three_element_container_instance); + } +}; + +template +class instantiator : public instantiator_impl {}; + +template +class move_only_view_instantiator : public instantiator_impl {}; + +template class InstantiatorType> +constexpr bool instantiation_test_for_category() { + // Unlike previous tested range/view types, views::zip takes an unbounded parameter pack of ranges, so + // we cannot test every possible combination of sensitive inputs. However, every evaluated condition + // other than sizeof... is evaluated as (Condition && ...); so, if one condition fails, then the entire + // constraint should fail to be met. + + using test::Sized, test::Common, test::CanDifference; + + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + InstantiatorType::call(); + + return true; +} + +template