diff --git a/llvm-project b/llvm-project index 634a0acb307..d66428cb995 160000 --- a/llvm-project +++ b/llvm-project @@ -1 +1 @@ -Subproject commit 634a0acb307ddad21c5542dc313e02b4df9b216e +Subproject commit d66428cb995c7a04ecb02951d5021d815fc02b2b diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 51bd4623c02..a8bd4ef18fb 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -89,9 +89,8 @@ namespace ranges { [[no_unique_address]] _In in; [[no_unique_address]] _Fn fun; - // clang-format off template <_Convertible_from _IIn, _Convertible_from _FFn> - constexpr operator in_fun_result<_IIn, _FFn>() const & { + constexpr operator in_fun_result<_IIn, _FFn>() const& { return {in, fun}; } @@ -99,7 +98,6 @@ namespace ranges { constexpr operator in_fun_result<_IIn, _FFn>() && { return {_STD move(in), _STD move(fun)}; } - // clang-format on }; // STRUCT TEMPLATE in_in_result @@ -108,9 +106,8 @@ namespace ranges { [[no_unique_address]] _In1 in1; [[no_unique_address]] _In2 in2; - // clang-format off template <_Convertible_from _IIn1, _Convertible_from _IIn2> - constexpr operator in_in_result<_IIn1, _IIn2>() const & { + constexpr operator in_in_result<_IIn1, _IIn2>() const& { return {in1, in2}; } @@ -118,7 +115,6 @@ namespace ranges { constexpr operator in_in_result<_IIn1, _IIn2>() && { return {_STD move(in1), _STD move(in2)}; } - // clang-format on }; // STRUCT TEMPLATE in_out_result @@ -127,9 +123,8 @@ namespace ranges { [[no_unique_address]] _In in; [[no_unique_address]] _Out out; - // clang-format off template <_Convertible_from _IIn, _Convertible_from _OOut> - constexpr operator in_out_result<_IIn, _OOut>() const & { + constexpr operator in_out_result<_IIn, _OOut>() const& { return {in, out}; } @@ -137,7 +132,6 @@ namespace ranges { constexpr operator in_out_result<_IIn, _OOut>() && { return {_STD move(in), _STD move(out)}; } - // clang-format on }; // STRUCT TEMPLATE in_in_out_result @@ -147,10 +141,9 @@ namespace ranges { [[no_unique_address]] _In2 in2; [[no_unique_address]] _Out out; - // clang-format off template <_Convertible_from _IIn1, _Convertible_from _IIn2, _Convertible_from _OOut> - constexpr operator in_in_out_result<_IIn1, _IIn2, _OOut>() const & { + constexpr operator in_in_out_result<_IIn1, _IIn2, _OOut>() const& { return {in1, in2, out}; } @@ -158,7 +151,6 @@ namespace ranges { constexpr operator in_in_out_result<_IIn1, _IIn2, _OOut>() && { return {_STD move(in1), _STD move(in2), _STD move(out)}; } - // clang-format on }; // STRUCT TEMPLATE in_out_out_result @@ -168,10 +160,9 @@ namespace ranges { [[no_unique_address]] _Out1 out1; [[no_unique_address]] _Out2 out2; - // clang-format off template <_Convertible_from _IIn, _Convertible_from _OOut1, _Convertible_from _OOut2> - constexpr operator in_out_out_result<_IIn, _OOut1, _OOut2>() const & { + constexpr operator in_out_out_result<_IIn, _OOut1, _OOut2>() const& { return {in, out1, out2}; } @@ -179,7 +170,6 @@ namespace ranges { constexpr operator in_out_out_result<_IIn, _OOut1, _OOut2>() && { return {_STD move(in), _STD move(out1), _STD move(out2)}; } - // clang-format on }; // STRUCT TEMPLATE min_max_result @@ -188,9 +178,8 @@ namespace ranges { [[no_unique_address]] _Ty min; [[no_unique_address]] _Ty max; - // clang-format off template <_Convertible_from _Ty2> - constexpr operator min_max_result<_Ty2>() const & { + constexpr operator min_max_result<_Ty2>() const& { return {min, max}; } @@ -198,7 +187,6 @@ namespace ranges { constexpr operator min_max_result<_Ty2>() && { return {_STD move(min), _STD move(max)}; } - // clang-format on }; // STRUCT TEMPLATE in_found_result @@ -207,9 +195,8 @@ namespace ranges { [[no_unique_address]] _In in; bool found; - // clang-format off template <_Convertible_from _IIn> - constexpr operator in_found_result<_IIn>() const & { + constexpr operator in_found_result<_IIn>() const& { return {in, found}; } @@ -217,7 +204,6 @@ namespace ranges { constexpr operator in_found_result<_IIn>() && { return {_STD move(in), found}; } - // clang-format on }; #ifdef __clang__ @@ -379,7 +365,6 @@ namespace ranges { public: using _Not_quite_object::_Not_quite_object; - // clang-format off template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { @@ -401,7 +386,6 @@ namespace ranges { _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); } - // clang-format on }; inline constexpr _Find_if_fn find_if{_Not_quite_object::_Construct_tag{}}; @@ -437,7 +421,6 @@ namespace ranges { public: using _Not_quite_object::_Not_quite_object; - // clang-format off template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { @@ -459,7 +442,6 @@ namespace ranges { _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); } - // clang-format on }; inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; @@ -611,7 +593,6 @@ namespace ranges { public: using _Not_quite_object::_Not_quite_object; - // clang-format off template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { @@ -633,7 +614,6 @@ namespace ranges { _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); } - // clang-format on }; inline constexpr _Count_if_fn count_if{_Not_quite_object::_Construct_tag{}}; @@ -946,11 +926,10 @@ namespace ranges { if (_Count != static_cast<_Size2>(_RANGES size(_Range2))) { return false; } - return _Equal_count(_RANGES _Ubegin(_Range1), _RANGES _Ubegin(_Range2), - _Count, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Equal_count(_Ubegin(_Range1), _Ubegin(_Range2), _Count, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } else { - return _Equal_4(_RANGES _Ubegin(_Range1), _RANGES _Uend(_Range1), - _RANGES _Ubegin(_Range2), _RANGES _Uend(_Range2), + return _Equal_4(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } } @@ -961,6 +940,340 @@ namespace ranges { } // namespace ranges #endif // __cpp_lib_concepts +// FUNCTION TEMPLATE is_permutation +template +_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Pr _Pred) { + // test if [_First1, _Last1) == permuted [_First2, ...), using _Pred + _Adl_verify_range(_First1, _Last1); + auto _UFirst1 = _Get_unwrapped(_First1); + const auto _ULast1 = _Get_unwrapped(_Last1); + auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); + + for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix + if (_UFirst1 == _ULast1) { // everything matched + return true; + } + + if (!_Pred(*_UFirst1, *_UFirst2)) { // found first inequality, check match counts in suffix + break; + } + } + + // Narrowing _Iter_diff_t<_FwdIt1> to _Iter_diff_t<_FwdIt2> is OK because the second range must be at least as long + // as the first. + const auto _Dist2 = static_cast<_Iter_diff_t<_FwdIt2>>(_STD distance(_UFirst1, _ULast1)); + return _Check_match_counts(_UFirst1, _ULast1, _UFirst2, _STD next(_UFirst2, _Dist2), _Pass_fn(_Pred)); +} + +template +_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2) { + // test if [_First1, _Last1) == permuted [_First2, ...) + return _STD is_permutation(_First1, _Last1, _First2, equal_to<>{}); +} + +template +_CONSTEXPR20 bool _Is_permutation_unchecked(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred, + forward_iterator_tag, forward_iterator_tag) { + // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred, arbitrary iterators + for (;; ++_First1, (void) ++_First2) { // trim matching prefix + if (_First1 == _Last1) { + return _First2 == _Last2; + } + + if (_First2 == _Last2) { + return false; + } + + if (!_Pred(*_First1, *_First2)) { // found first inequality, check match counts in suffix + break; + } + } + + auto _Next1 = _First1; + auto _Next2 = _First2; + for (;; ++_Next1, (void) ++_Next2) { // check for same lengths + if (_Next1 == _Last1) { + if (_Next2 == _Last2) { + return _Check_match_counts(_First1, _Last1, _First2, _Last2, _Pred); + } + + return false; // sequence 1 is shorter than sequence 2, not a permutation + } + + if (_Next2 == _Last2) { + return false; // sequence 1 is longer than sequence 2, not a permutation + } + } +} + +template +_CONSTEXPR20 bool _Is_permutation_unchecked(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred, + random_access_iterator_tag, random_access_iterator_tag) { + // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred, random-access iterators + if (_Last1 - _First1 != _Last2 - _First2) { + return false; + } + + for (; _First1 != _Last1; ++_First1, (void) ++_First2) { // trim matching prefix + if (!_Pred(*_First1, *_First2)) { + // found first inequality, check match counts in suffix + return _Check_match_counts(_First1, _Last1, _First2, _Last2, _Pred); + } + } + + return true; +} + +template +_NODISCARD _CONSTEXPR20 bool is_permutation( + _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred) { + // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + return _Is_permutation_unchecked(_Get_unwrapped(_First1), _Get_unwrapped(_Last1), _Get_unwrapped(_First2), + _Get_unwrapped(_Last2), _Pass_fn(_Pred), _Iter_cat_t<_FwdIt1>{}, _Iter_cat_t<_FwdIt2>{}); +} + +template +_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2) { + // test if [_First1, _Last1) == permuted [_First2, _Last2) + return _STD is_permutation(_First1, _Last1, _First2, _Last2, equal_to<>{}); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + // clang-format off + template + concept _Bidi_common = is_same_v<_It, _Se> && bidirectional_iterator<_It>; + template + concept _Bidi_common_range = common_range<_Rng> && bidirectional_iterator>; + // clang-format on + + // VARIABLE ranges::is_permutation + class _Is_permutation_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se1, forward_iterator _It2, sentinel_for<_It2> _Se2, + class _Pj1 = identity, class _Pj2 = identity, + indirect_equivalence_relation, projected<_It2, _Pj2>> _Pr = ranges::equal_to> + _NODISCARD constexpr bool operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, + _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + const auto _Count = _ULast1 - _UFirst1; + if (_ULast2 - _UFirst2 != _Count) { + return false; + } + + return _Is_permutation_sized(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Count, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Is_permutation_unsized(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + + template , _Pj1>, projected, _Pj2>> _Pr = + ranges::equal_to> + _NODISCARD constexpr bool operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + const auto _Count = _RANGES distance(_Range1); + if (_RANGES distance(_Range2) != _Count) { + return false; + } + + return _Is_permutation_sized(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Count, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Is_permutation_unsized(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + + private: + template + _NODISCARD static constexpr bool _Is_permutation_sized(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT( + indirect_equivalence_relation<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + _STL_INTERNAL_CHECK(_RANGES distance(_First1, _Last1) == _Count); + _STL_INTERNAL_CHECK(_RANGES distance(_First2, _Last2) == _Count); + + for (;; ++_First1, (void) ++_First2, --_Count) { // trim matching prefixes + if (_Count == 0) { // everything matched + return true; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { // mismatch + break; + } + } + + if (_Count == 1) { // single non-matching elements remain; not a permutation + return false; + } + // If we get here, _Count > 1 and initial elements do not match. + + if constexpr (bidirectional_iterator<_It1> && bidirectional_iterator<_It2>) { + // determine final iterator values + auto _Final1 = _Find_last_iterator(_First1, _Last1, _Count); + auto _Final2 = _Find_last_iterator(_First2, _Last2, _Count); + + for (;;) { // trim matching suffixes + --_Final1; + --_Final2; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch + ++_Final1; + ++_Final2; + break; + } + + if (--_Count == 1) { + return false; // initial elements still do not match + } + } + // If we get here, _Count > 1, initial elements do not match, and final elements do not match. + + return _Match_counts(_STD move(_First1), _STD move(_Final1), _STD move(_First2), _STD move(_Final2), + _Pred, _Proj1, _Proj2); + } else { + return _Match_counts(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2), + _Pred, _Proj1, _Proj2); + } + } + + template + _NODISCARD static constexpr bool _Is_permutation_unsized( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT( + indirect_equivalence_relation<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + for (;; ++_First1, (void) ++_First2) { // trim matching prefixes + if (_First1 == _Last1) { // first range is a prefix of second + return _First2 == _Last2; + } else if (_First2 == _Last2) { // second range is a proper prefix of first + return false; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { // mismatch + break; + } + } + // If we get here, initial elements do not match. + + // determine final iterator values and validate lengths + auto _Final1 = _First1; + auto _Final2 = _First2; + for (;;) { + ++_Final1; + ++_Final2; + if (_Final1 == _Last1) { + if (_Final2 == _Last2) { + break; // equal lengths + } + + return false; // different lengths; not a permutation + } else if (_Final2 == _Last2) { + return false; // ditto different lengths + } + } + // If we get here, initial elements do not match and ranges have equal lengths. + + if constexpr (bidirectional_iterator<_It1> && bidirectional_iterator<_It2>) { + for (;;) { // trim matching suffixes + if (--_Final1 == _First1) { + return false; // initial elements still do not match + } + --_Final2; // since ranges have equal lengths, _Final2 cannot equal _First2 + + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch + ++_Final1; + ++_Final2; + break; + } + } + // If we get here, initial elements do not match, final elements do not match, and ranges have length + // at least 2. + } + + return _Match_counts( + _STD move(_First1), _STD move(_Final1), _STD move(_First2), _STD move(_Final2), _Pred, _Proj1, _Proj2); + } + + template + _NODISCARD static constexpr bool _Match_counts(const _It1 _First1, const _Se1 _Last1, const _It2 _First2, + const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT( + indirect_equivalence_relation<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + for (auto _Current = _First1; _Current != _Last1; ++_Current) { + bool _Found = false; + auto _Mid1 = _First1; + for (; _Mid1 != _Current; ++_Mid1) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_Current), _STD invoke(_Proj1, *_Mid1))) { + // this value appears earlier in the first range so we've already counted occurrences + _Found = true; + break; + } + } + + if (_Found) { + continue; + } + + // count occurrences of this value in the first range + iter_difference_t<_It1> _Occurrences = 1; + while (++_Mid1 != _Last1) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_Current), _STD invoke(_Proj1, *_Mid1))) { + ++_Occurrences; + } + } + + // subtract occurrences in the second range + for (auto _Mid2 = _First2; _Mid2 != _Last2; ++_Mid2) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_Current), _STD invoke(_Proj2, *_Mid2))) { + if (--_Occurrences < 0) { + // value appears more in second range than first; not a permutation + return false; + } + } + } + + if (_Occurrences != 0) { + // value appears more in first range than second; not a permutation + return false; + } + } + + return true; + } + }; + + inline constexpr _Is_permutation_fn is_permutation{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE all_of template _NODISCARD _CONSTEXPR20 bool all_of(_InIt _First, _InIt _Last, _Pr _Pred) { // test if all elements satisfy _Pred @@ -2082,8 +2395,7 @@ namespace ranges { && random_access_iterator<_It2> && sized_sentinel_for<_Se2, _It2>) { return _Random_access_sized_ranges(_First1, _Last1 - _First1, _First2, _Last2 - _First2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } else if constexpr (bidirectional_iterator<_It1> && is_same_v<_It1, _Se1> - && bidirectional_iterator<_It2> && is_same_v<_It2, _Se2>) { + } else if constexpr (_Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>) { return _Bidi_common_ranges(_First1, _Last1, _First2, _Last2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } else { @@ -2103,8 +2415,7 @@ namespace ranges { _RANGES begin(_Range1), _RANGES distance(_Range1), _RANGES begin(_Range2), _RANGES distance(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } else if constexpr (bidirectional_range<_Rng1> && common_range<_Rng1> - && bidirectional_range<_Rng2> && common_range<_Rng2>) { + } else if constexpr (_Bidi_common_range<_Rng1> && _Bidi_common_range<_Rng2>) { return _Bidi_common_ranges( _RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2), diff --git a/stl/inc/xutility b/stl/inc/xutility index f279fff2727..c4f918984bd 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -921,6 +921,28 @@ concept indirect_binary_predicate = indirectly_readable<_It1> && predicate<_Fn&, iter_reference_t<_It1>, iter_reference_t<_It2>> && predicate<_Fn&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>; +// CONCEPT indirect_equivalence_relation +template +concept indirect_equivalence_relation = indirectly_readable<_It1> + && indirectly_readable<_It2> + && copy_constructible<_Fn> + && equivalence_relation<_Fn&, iter_value_t<_It1>&, iter_value_t<_It2>&> + && equivalence_relation<_Fn&, iter_value_t<_It1>&, iter_reference_t<_It2>> + && equivalence_relation<_Fn&, iter_reference_t<_It1>, iter_value_t<_It2>&> + && equivalence_relation<_Fn&, iter_reference_t<_It1>, iter_reference_t<_It2>> + && equivalence_relation<_Fn&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>; + +// CONCEPT indirect_strict_weak_order +template +concept indirect_strict_weak_order = indirectly_readable<_It1> + && indirectly_readable<_It2> + && copy_constructible<_Fn> + && strict_weak_order<_Fn&, iter_value_t<_It1>&, iter_value_t<_It2>&> + && strict_weak_order<_Fn&, iter_value_t<_It1>&, iter_reference_t<_It2>> + && strict_weak_order<_Fn&, iter_reference_t<_It1>, iter_value_t<_It2>&> + && strict_weak_order<_Fn&, iter_reference_t<_It1>, iter_reference_t<_It2>> + && strict_weak_order<_Fn&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>; + // ALIAS TEMPLATE indirect_result_t template requires (indirectly_readable<_Its> && ...) @@ -987,6 +1009,11 @@ namespace ranges { concept _Can_swap_references = indirectly_readable> && indirectly_readable> && swappable_with, iter_reference_t<_Ty2>>; + + template + concept _Symmetric_indirectly_movable_storable = + indirectly_movable_storable, remove_reference_t<_Ty2>> + && indirectly_movable_storable, remove_reference_t<_Ty1>>; // clang-format on template @@ -1001,15 +1028,13 @@ namespace ranges { private: enum class _St { _None, _Custom, _Swap, _Exchange }; - // clang-format off template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { if constexpr (_Has_ADL<_Ty1, _Ty2>) { return {_St::_Custom, noexcept(iter_swap(_STD declval<_Ty1>(), _STD declval<_Ty2>()))}; } else if constexpr (_Can_swap_references<_Ty1, _Ty2>) { return {_St::_Swap, noexcept(_RANGES swap(*_STD declval<_Ty1>(), *_STD declval<_Ty2>()))}; - } else if constexpr (indirectly_movable_storable, remove_reference_t<_Ty2>> - && indirectly_movable_storable, remove_reference_t<_Ty1>>) { + } else if constexpr (_Symmetric_indirectly_movable_storable<_Ty1, _Ty2>) { constexpr auto _Nothrow = noexcept( *_STD declval<_Ty1>() = _Iter_exchange_move(_STD declval<_Ty1>(), _STD declval<_Ty2>())); return {_St::_Exchange, _Nothrow}; @@ -1022,6 +1047,7 @@ namespace ranges { static constexpr _Choice_t<_St> _Choice = _Choose<_Ty1, _Ty2>(); public: + // clang-format off template requires (_Choice<_Ty1, _Ty2>._Strategy != _St::_None) constexpr void operator()(_Ty1&& _Val1, _Ty2&& _Val2) const noexcept(_Choice<_Ty1, _Ty2>._No_throw) { @@ -3261,6 +3287,19 @@ namespace ranges { // VARIABLE ranges::prev inline constexpr _Prev_fn prev{_Not_quite_object::_Construct_tag{}}; + // FUNCTION TEMPLATE _Find_last_iterator + template _Se> + _NODISCARD constexpr _It _Find_last_iterator( + const _It& _First, const _Se& _Last, const iter_difference_t<_It> _Count) { + // Find the iterator in [_First, _Last) (of length _Count) which equals _Last + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + if constexpr (is_same_v<_It, _Se>) { + return _Last; + } else { + return _RANGES next(_First, _Count); + } + } + // STRUCT ranges::equal_to struct equal_to { // clang-format off @@ -5346,7 +5385,7 @@ _NODISCARD _Iter_diff_t<_FwdIt> count( _ExPo&& _Exec, const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) noexcept; // terminates #endif // _HAS_CXX17 -// FUNCTION TEMPLATE is_permutation +// FUNCTION TEMPLATE _Check_match_counts template _NODISCARD constexpr _InIt _Find_pr(_InIt _First, const _InIt _Last, const _Ty& _Val, _Pr _Pred) { for (; _First != _Last; ++_First) { @@ -5427,114 +5466,6 @@ _NODISCARD _CONSTEXPR20 bool _Check_match_counts( return true; } -template -_NODISCARD _CONSTEXPR20 bool _Is_permutation_unchecked(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Pr _Pred) { - // test if [_First1, _Last1) == permuted [_First2, ...), using _Pred - for (; _First1 != _Last1; ++_First1, (void) ++_First2) { // trim matching prefix - if (!_Pred(*_First1, *_First2)) { - // found first inequality, check match counts in suffix - // - // narrowing _Iter_diff_t<_FwdIt1> to _Iter_diff_t<_FwdIt2> is OK because if the second range is shorter - // than the first, the user already triggered UB - auto _Last2 = _STD next(_First2, static_cast<_Iter_diff_t<_FwdIt2>>(_STD distance(_First1, _Last1))); - return _Check_match_counts(_First1, _Last1, _First2, _Last2, _Pred); - } - } - - return true; -} - -template -_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Pr _Pred) { - // test if [_First1, _Last1) == permuted [_First2, ...), using _Pred - _Adl_verify_range(_First1, _Last1); - const auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); - return _Is_permutation_unchecked(_UFirst1, _ULast1, _UFirst2, _Pass_fn(_Pred)); -} - -template -_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2) { - // test if [_First1, _Last1) == permuted [_First2, ...) - return _STD is_permutation(_First1, _Last1, _First2, equal_to<>()); -} - -template -_CONSTEXPR20 bool _Is_permutation_unchecked(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred, - forward_iterator_tag, forward_iterator_tag) { - // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred, arbitrary iterators - for (;;) { // trim matching prefix - if (_First1 == _Last1) { - return _First2 == _Last2; - } - - if (_First2 == _Last2) { - return false; - } - - if (!_Pred(*_First1, *_First2)) { // found first inequality, check match counts in suffix - break; - } - - ++_First1; - ++_First2; - } - - auto _Next1 = _First1; - auto _Next2 = _First2; - for (;;) { // check for same lengths - if (_Next1 == _Last1) { - if (_Next2 == _Last2) { - return _Check_match_counts(_First1, _Last1, _First2, _Last2, _Pred); - } - - return false; // sequence 1 is shorter than sequence 2, not a permutation - } - - if (_Next2 == _Last2) { - return false; // sequence 1 is longer than sequence 2, not a permutation - } - - ++_Next1; - ++_Next2; - } -} - -template -_CONSTEXPR20 bool _Is_permutation_unchecked(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred, - random_access_iterator_tag, random_access_iterator_tag) { - // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred, random-access iterators - if (_Last1 - _First1 != _Last2 - _First2) { - return false; - } - - for (; _First1 != _Last1; ++_First1, (void) ++_First2) { // trim matching prefix - if (!_Pred(*_First1, *_First2)) { - // found first inequality, check match counts in suffix - return _Check_match_counts(_First1, _Last1, _First2, _Last2, _Pred); - } - } - - return true; -} - -template -_NODISCARD _CONSTEXPR20 bool is_permutation( - _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred) { - // test if [_First1, _Last1) == permuted [_First2, _Last2), using _Pred - _Adl_verify_range(_First1, _Last1); - _Adl_verify_range(_First2, _Last2); - return _Is_permutation_unchecked(_Get_unwrapped(_First1), _Get_unwrapped(_Last1), _Get_unwrapped(_First2), - _Get_unwrapped(_Last2), _Pass_fn(_Pred), _Iter_cat_t<_FwdIt1>(), _Iter_cat_t<_FwdIt2>()); -} - -template -_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2) { - // test if [_First1, _Last1) == permuted [_First2, _Last2) - return _STD is_permutation(_First1, _Last1, _First2, _Last2, equal_to<>()); -} - // FUNCTION TEMPLATE reverse #if _HAS_IF_CONSTEXPR template diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 1e1d62bcd84..5386ea4ba97 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -583,11 +583,11 @@ std/containers/sequences/array/array.fill/fill.fail.cpp FAIL std/containers/sequences/array/array.swap/swap.fail.cpp FAIL # STL bug: VSO-207715 We reject array. -std/containers/sequences/array/array.cons/default.pass.cpp FAIL std/containers/sequences/array/array.cons/implicit_copy.pass.cpp FAIL +std/containers/sequences/array/array.cons/initialization.pass.cpp FAIL std/containers/sequences/array/array.data/data_const.pass.cpp FAIL std/containers/sequences/array/array.data/data.pass.cpp FAIL -std/containers/sequences/array/begin.pass.cpp FAIL +std/containers/sequences/array/iterators.pass.cpp FAIL # STL bug: string_view doesn't enforce "non-array trivial standard-layout", related to LWG-3034. std/strings/string.view/char.bad.fail.cpp:0 FAIL diff --git a/tests/libcxx/magic_comments.txt b/tests/libcxx/magic_comments.txt index a905db0a4e7..b68a41e8387 100644 --- a/tests/libcxx/magic_comments.txt +++ b/tests/libcxx/magic_comments.txt @@ -1,6 +1,6 @@ +// REQUIRES: c++03 +// REQUIRES: c++03 || c++11 || c++14 +// REQUIRES: c++03 || c++11 || c++14 || c++17 // REQUIRES: c++11 // REQUIRES: c++11 || c++14 -// REQUIRES: c++98 || c++03 -// REQUIRES: c++98 || c++03 || c++11 || c++14 -// REQUIRES: c++98 || c++03 || c++11 || c++14 || c++17 // UNSUPPORTED: c++14, c++17, c++2a diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 17da4731102..7a877efccc5 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -583,11 +583,11 @@ containers\sequences\array\array.fill\fill.fail.cpp containers\sequences\array\array.swap\swap.fail.cpp # STL bug: VSO-207715 We reject array. -containers\sequences\array\array.cons\default.pass.cpp containers\sequences\array\array.cons\implicit_copy.pass.cpp +containers\sequences\array\array.cons\initialization.pass.cpp containers\sequences\array\array.data\data_const.pass.cpp containers\sequences\array\array.data\data.pass.cpp -containers\sequences\array\begin.pass.cpp +containers\sequences\array\iterators.pass.cpp # STL bug: string_view doesn't enforce "non-array trivial standard-layout", related to LWG-3034. strings\string.view\char.bad.fail.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index e0c06acf279..07a87cb3177 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -249,6 +249,7 @@ tests\P0896R4_ranges_alg_find_if tests\P0896R4_ranges_alg_find_if_not tests\P0896R4_ranges_alg_for_each tests\P0896R4_ranges_alg_for_each_n +tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_mismatch tests\P0896R4_ranges_alg_move tests\P0896R4_ranges_alg_none_of diff --git a/tests/std/tests/P0896R4_ranges_alg_is_permutation/env.lst b/tests/std/tests/P0896R4_ranges_alg_is_permutation/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_is_permutation/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp b/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp new file mode 100644 index 00000000000..9cd422f5b6f --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +// +#include + +using namespace std; + +using P = pair; + +struct instantiator { + static constexpr array pairs = { + P{0, 42}, P{0, 42}, P{1, 42}, P{1, 42}, P{0, 42}, P{0, 42}, P{1, 42}, P{1, 42}}; + static constexpr array not_pairs = {0, 0, 0, 0, 1, 1, 1, 1}; + static constexpr array too_few_ones = {0, 0, 0, 0, 0, 1, 1, 1}; + static constexpr array too_many_ones = {0, 0, 0, 1, 1, 1, 1, 1}; + + template + static constexpr void call() { + const Fwd1 range1{pairs}; + const Fwd2 range2{not_pairs}; + + // defaulted predicate and projections + assert(ranges::is_permutation(range1, range1)); + assert(ranges::is_permutation( + ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2))); + + // explicit predicate + assert(ranges::is_permutation(range2, range2, ranges::equal_to{})); + assert(ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(range1), + ranges::end(range1), ranges::equal_to{})); + + // explicit predicate and one projection + assert(ranges::is_permutation(range1, range2, ranges::equal_to{}, get_first)); + assert(ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), ranges::equal_to{}, get_first)); + + // explicit predicate and two projections + assert(ranges::is_permutation(range1, range2, ranges::equal_to{}, get_first, identity{})); + assert(ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), ranges::equal_to{}, get_first, identity{})); + + { + // negative case [different lengths] + const Fwd2 suffix2{std::span{not_pairs}.subspan<2>()}; + assert(!ranges::is_permutation(range1, suffix2, ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(suffix2, range1, ranges::equal_to{}, identity{}, get_first)); + assert(!ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(suffix2), + ranges::end(suffix2), ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(ranges::begin(suffix2), ranges::end(suffix2), ranges::begin(range1), + ranges::end(range1), ranges::equal_to{}, identity{}, get_first)); + } + { + // negative case [1 appears more in first range than second] + const Fwd2 r2{too_few_ones}; + assert(!ranges::is_permutation(range1, r2, ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(r2), + ranges::end(r2), ranges::equal_to{}, get_first, identity{})); + } + { + // negative case [1 appears more in second range than first] + const Fwd2 r2{too_many_ones}; + assert(!ranges::is_permutation(range1, r2, ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(ranges::begin(range1), ranges::end(range1), ranges::begin(r2), + ranges::end(r2), ranges::equal_to{}, get_first, identity{})); + } + + { + // negative case [only final elements differ] + const Fwd1 r1{std::span{pairs}.subspan<0, 3>()}; + const Fwd2 r2{std::span{not_pairs}.subspan<0, 3>()}; + assert(!ranges::is_permutation(r1, r2, ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2), + ranges::equal_to{}, get_first, identity{})); + } + { + // negative case [only initial elements differ] + const Fwd1 r1{std::span{pairs}.subspan<1, 3>()}; + const Fwd2 r2{std::span{not_pairs}.subspan<4, 3>()}; + assert(!ranges::is_permutation(r1, r2, ranges::equal_to{}, get_first, identity{})); + assert(!ranges::is_permutation(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2), + ranges::equal_to{}, get_first, identity{})); + } + } +}; + +#ifdef TEST_EVERYTHING +int main() { + // No constexpr test here; this test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation. + test_fwd_fwd(); +} +#else // ^^^ test all range combinations // test only interesting range combos vvv +template +using fwd_test_range = test::range; +template +using bidi_test_range = test::range; + +constexpr bool run_tests() { + using Elem1 = const P; + using Elem2 = const int; + + // All proxy reference types, since the algorithm doesn't really care. + using test::Common, test::Sized; + + // both forward non-common and 0-/1-/2-sized + instantiator::call, fwd_test_range>(); + instantiator::call, fwd_test_range>(); + instantiator::call, fwd_test_range>(); + instantiator::call, fwd_test_range>(); + + // both bidi same common and 0-/1-/2-sized + instantiator::call, bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + + instantiator::call, + bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + instantiator::call, + bidi_test_range>(); + + return true; +} + +int main() { + STATIC_ASSERT(run_tests()); + run_tests(); +} +#endif // TEST_EVERYTHING diff --git a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp index d665e3cc7ad..192aed0c171 100644 --- a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp @@ -164,8 +164,7 @@ namespace indirect_unary_predicate_test { } // namespace indirect_unary_predicate_test namespace indirect_binary_predicate_test { - // Also validate indirect_result_t - using std::indirect_binary_predicate; + // Also validate indirect_equivalence_relation, indirect_strict_weak_order, and indirect_result_t template struct base {}; @@ -196,15 +195,29 @@ namespace indirect_binary_predicate_test { std::false_type operator()(simple_common_reference, simple_common_reference) const requires(I != 5); }; - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<0>, simple_iter_archetype<1>>); - STATIC_ASSERT(!indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<0>>); - STATIC_ASSERT(indirect_binary_predicate, simple_iter_archetype<1>, simple_iter_archetype<1>>); + template + constexpr bool test() { + using std::indirect_binary_predicate, std::indirect_equivalence_relation, std::indirect_strict_weak_order; + using F = Fn; + using I1 = simple_iter_archetype; + using I2 = simple_iter_archetype; + + constexpr bool result = indirect_binary_predicate; + STATIC_ASSERT(indirect_equivalence_relation == result); + STATIC_ASSERT(indirect_strict_weak_order == result); + return result; + } + + STATIC_ASSERT(!test<0, 1, 1>()); + STATIC_ASSERT(!test<1, 1, 1>()); + STATIC_ASSERT(!test<2, 1, 1>()); + STATIC_ASSERT(!test<3, 1, 1>()); + STATIC_ASSERT(!test<4, 1, 1>()); + STATIC_ASSERT(!test<5, 1, 1>()); + + STATIC_ASSERT(!test<6, 0, 1>()); + STATIC_ASSERT(!test<6, 1, 0>()); + STATIC_ASSERT(test<6, 1, 1>()); STATIC_ASSERT(std::same_as, simple_iter_archetype<1>, simple_iter_archetype<1>>, std::true_type>); diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 46a592a9d40..31143faaa0a 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -796,7 +796,7 @@ namespace iterator_traits_test { }; // clang-format off - template + template concept has_empty_traits = !(requires { typename iterator_traits::iterator_concept; } || requires { typename iterator_traits::iterator_category; } || requires { typename iterator_traits::value_type; } @@ -1700,7 +1700,7 @@ namespace unreachable_sentinel_test { STATIC_ASSERT(std::is_nothrow_move_assignable_v); // clang-format off - template + template concept Comparable = requires(T const& t) { { t == unreachable_sentinel } -> std::same_as; { t != unreachable_sentinel } -> std::same_as; diff --git a/tests/std/tests/P0896R4_ranges_subrange/test.cpp b/tests/std/tests/P0896R4_ranges_subrange/test.cpp index 1716ef933a7..8ea40d56f81 100644 --- a/tests/std/tests/P0896R4_ranges_subrange/test.cpp +++ b/tests/std/tests/P0896R4_ranges_subrange/test.cpp @@ -1400,8 +1400,7 @@ namespace test_subrange { STATIC_ASSERT(test_ctad>()); - STATIC_ASSERT( - test_ctad>()); // FIXME: look at these + STATIC_ASSERT(test_ctad>()); STATIC_ASSERT(test_ctad>()); STATIC_ASSERT(test_ctad>()); STATIC_ASSERT(test_ctad>()); diff --git a/tests/std/tests/P0898R3_concepts/test.cpp b/tests/std/tests/P0898R3_concepts/test.cpp index 68f4a4c3fd1..41a27ab4f63 100644 --- a/tests/std/tests/P0898R3_concepts/test.cpp +++ b/tests/std/tests/P0898R3_concepts/test.cpp @@ -2110,12 +2110,12 @@ namespace test_swappable_with { // clang-format off namespace ranges = std::ranges; - template U> + template U> void value_swap(T&& t, U&& u) { ranges::swap(std::forward(t), std::forward(u)); } - template + template void lv_swap(T& t1, T& t2) { ranges::swap(t1, t2); }