Implement ranges min/max/minmax algorithms (#916)

* `ranges::min` (direct, `range`, and `initializer_list` overloads)
* `ranges::max` (direct, `range`, and `initializer_list` overloads)
* `ranges::minmax` (direct, `range`, and `initializer_list` overloads)
* `ranges::min_element` (`range` only)
* `ranges::max_element` (`range` only)
* `ranges::minmax_element` (`range` only)
* `ranges::clamp` (direct only)

Implements concept `indirectly_copyable_storable`, and adds test coverage for both that concept and `indirectly_movable_storable`.
This commit is contained in:
Casey Carter 2020-06-29 22:39:38 -07:00 коммит произвёл GitHub
Родитель 021af3e68e
Коммит c114d51fd1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 1009 добавлений и 39 удалений

Просмотреть файл

@ -5477,6 +5477,56 @@ _NODISCARD _FwdIt max_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /*
// 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 {
// VARIABLE ranges::max_element
template <class _It, class _Se, class _Pr, class _Pj>
_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>>);
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 <forward_iterator _It, sentinel_for<_It> _Se, class _Pj = identity,
indirect_strict_weak_order<projected<_It, _Pj>> _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(_Get_unwrapped(_STD move(_First)),
_Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)));
return _First;
}
template <forward_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _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(_Get_unwrapped(_STD move(_First)), _Uend(_Range),
_Pass_fn(_Pred), _Pass_fn(_Proj)));
return _First;
}
};
inline constexpr _Max_element_fn max_element{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17
// FUNCTION TEMPLATE min_element
@ -5520,6 +5570,56 @@ _NODISCARD _FwdIt min_element(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /*
// 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 {
// VARIABLE ranges::min_element
template <class _It, class _Se, class _Pr, class _Pj>
_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>>);
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 <forward_iterator _It, sentinel_for<_It> _Se, class _Pj = identity,
indirect_strict_weak_order<projected<_It, _Pj>> _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(_Get_unwrapped(_STD move(_First)),
_Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)));
return _First;
}
template <forward_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _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(_Get_unwrapped(_STD move(_First)), _Uend(_Range),
_Pass_fn(_Pred), _Pass_fn(_Proj)));
return _First;
}
};
inline constexpr _Min_element_fn min_element{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17
// FUNCTION TEMPLATE minmax_element
@ -5592,6 +5692,96 @@ _NODISCARD pair<_FwdIt, _FwdIt> minmax_element(_ExPo&&, _FwdIt _First, _FwdIt _L
// not parallelized at present, parallelism expected to be feasible in a future release
return _STD minmax_element(_First, _Last);
}
#ifdef __cpp_lib_concepts
namespace ranges {
// ALIAS TEMPLATE minmax_element_result
template <class _It>
using minmax_element_result = min_max_result<_It>;
// VARIABLE ranges::minmax_element
template <class _It, class _Se, class _Pr, class _Pj>
constexpr min_max_result<_It> _Minmax_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>>);
min_max_result<_It> _Found{_First, _First};
if (_First == _Last) {
return _Found;
}
while (++_First != _Last) { // process one or two elements
_It _Prev = _First;
if (++_First == _Last) { // process last element
if (_STD invoke(_Pred, _STD invoke(_Proj, *_Prev), _STD invoke(_Proj, *_Found.min))) {
_Found.min = _Prev;
} else if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Prev), _STD invoke(_Proj, *_Found.max))) {
_Found.max = _Prev;
}
break;
}
// process next two elements
if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) {
// test _First for new smallest
if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Found.min))) {
_Found.min = _First;
}
if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Prev), _STD invoke(_Proj, *_Found.max))) {
_Found.max = _Prev;
}
} else { // test _Prev for new smallest
if (_STD invoke(_Pred, _STD invoke(_Proj, *_Prev), _STD invoke(_Proj, *_Found.min))) {
_Found.min = _Prev;
}
if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Found.max))) {
_Found.max = _First;
}
}
}
return _Found;
}
class _Minmax_element_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
template <forward_iterator _It, sentinel_for<_It> _Se, class _Pj = identity,
indirect_strict_weak_order<projected<_It, _Pj>> _Pr = ranges::less>
_NODISCARD constexpr minmax_element_result<_It> operator()(
_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _RANGES _Minmax_element_unchecked(
_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj));
_Seek_wrapped(_First, _STD move(_UResult.min));
auto _Second = _First;
_Seek_wrapped(_Second, _STD move(_UResult.max));
return {_STD move(_First), _STD move(_Second)};
}
template <forward_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _Pj>> _Pr = ranges::less>
_NODISCARD constexpr minmax_element_result<borrowed_iterator_t<_Rng>> operator()(
_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _RANGES _Minmax_element_unchecked(
_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj));
_Seek_wrapped(_First, _STD move(_UResult.min));
auto _Second = _First;
_Seek_wrapped(_Second, _STD move(_UResult.max));
return {_STD move(_First), _STD move(_Second)};
}
};
inline constexpr _Minmax_element_fn minmax_element{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17
// FUNCTION TEMPLATE max (for initializer_list)
@ -5608,6 +5798,72 @@ _NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist) {
return (_STD max)(_Ilist, less<>());
}
#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::max
// clang-format off
template <class _It>
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<iter_value_t<_It>>);
// clang-format on
class _Max_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
template <class _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _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 <copyable _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _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));
}
// clang-format off
template <input_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _Pj>> _Pr = ranges::less>
requires indirectly_copyable_storable<iterator_t<_Rng>, 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<iterator_t<_Rng>>) {
return static_cast<range_value_t<_Rng>>(*_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;
}
}
// clang-format on
};
inline constexpr _Max_fn max{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
// FUNCTION TEMPLATE min (for initializer_list)
template <class _Ty, class _Pr>
_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist, _Pr _Pred) {
@ -5622,6 +5878,64 @@ _NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist) {
return (_STD min)(_Ilist, less<>());
}
#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::min
class _Min_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
template <class _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _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 <copyable _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _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));
}
// clang-format off
template <input_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _Pj>> _Pr = ranges::less>
requires indirectly_copyable_storable<iterator_t<_Rng>, 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<iterator_t<_Rng>>) {
return static_cast<range_value_t<_Rng>>(*_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;
}
}
// clang-format on
};
inline constexpr _Min_fn min{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
// FUNCTION TEMPLATE minmax
template <class _Ty, class _Pr>
_NODISCARD constexpr pair<const _Ty&, const _Ty&> minmax(const _Ty& _Left, const _Ty& _Right, _Pr _Pred) noexcept(
@ -5659,6 +5973,102 @@ _NODISCARD constexpr pair<_Ty, _Ty> minmax(initializer_list<_Ty> _Ilist) {
return _STD minmax(_Ilist, less<>());
}
#ifdef __cpp_lib_concepts
namespace ranges {
// ALIAS TEMPLATE minmax_result
template <class _Ty>
using minmax_result = min_max_result<_Ty>;
// VARIABLE ranges::minmax
class _Minmax_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
template <class _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _Pr = ranges::less>
_NODISCARD constexpr minmax_result<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, _Left};
} else {
return {_Left, _Right};
}
}
template <copyable _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _Pr = ranges::less>
_NODISCARD constexpr minmax_result<_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::minmax must not be empty. (N4861 [alg.min.max]/21)");
const auto _Found = _RANGES _Minmax_element_unchecked(_First, _Last, _Pass_fn(_Pred), _Pass_fn(_Proj));
return {static_cast<_Ty>(*_Found.min), static_cast<_Ty>(*_Found.max)};
}
template <input_range _Rng, class _Pj = identity,
indirect_strict_weak_order<projected<iterator_t<_Rng>, _Pj>> _Pr = ranges::less>
requires indirectly_copyable_storable<iterator_t<_Rng>, range_value_t<_Rng>*>
_NODISCARD constexpr minmax_result<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::minmax must not be empty. (N4861 [alg.min.max]/21)");
using _Vty = range_value_t<_Rng>;
if constexpr (forward_range<_Rng> && _Prefer_iterator_copies<iterator_t<_Rng>>) {
const auto _Found = _RANGES _Minmax_element_unchecked(
_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj));
return {static_cast<_Vty>(*_Found.min), static_cast<_Vty>(*_Found.max)};
} else {
minmax_result<_Vty> _Found = {static_cast<_Vty>(*_UFirst), static_cast<_Vty>(*_UFirst)};
if (_UFirst == _ULast) {
return _Found;
}
while (++_UFirst != _ULast) { // process one or two elements
auto _Prev = *_UFirst;
if (++_UFirst == _ULast) { // process last element
if (_STD invoke(_Pred, _STD invoke(_Proj, _Prev), _STD invoke(_Proj, _Found.min))) {
_Found.min = _STD move(_Prev);
} else if (!_STD invoke(_Pred, _STD invoke(_Proj, _Prev), _STD invoke(_Proj, _Found.max))) {
_Found.max = _STD move(_Prev);
}
break;
}
// process next two elements
if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, _Prev))) {
// test _UFirst for new smallest
if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, _Found.min))) {
_Found.min = *_UFirst;
}
if (!_STD invoke(_Pred, _STD invoke(_Proj, _Prev), _STD invoke(_Proj, _Found.max))) {
_Found.max = _STD move(_Prev);
}
} else { // test _Prev for new smallest
if (_STD invoke(_Pred, _STD invoke(_Proj, _Prev), _STD invoke(_Proj, _Found.min))) {
_Found.min = _STD move(_Prev);
}
if (!_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, _Found.max))) {
_Found.max = *_UFirst;
}
}
}
return _Found;
}
}
};
inline constexpr _Minmax_fn minmax{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
// FUNCTION TEMPLATE next_permutation
template <class _BidIt, class _Pr>
_CONSTEXPR20 bool next_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) {
@ -5900,8 +6310,39 @@ _NODISCARD constexpr const _Ty& clamp(const _Ty& _Val, const _Ty& _Min_val, cons
template <class _Ty>
_NODISCARD constexpr const _Ty& clamp(const _Ty& _Val, const _Ty& _Min_val, const _Ty& _Max_val) {
// returns _Val constrained to [_Min_val, _Max_val]
return _STD clamp(_Val, _Min_val, _Max_val, less<>());
return _STD clamp(_Val, _Min_val, _Max_val, less{});
}
#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::clamp
class _Clamp_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
template <class _Ty, class _Pj = identity,
indirect_strict_weak_order<projected<const _Ty*, _Pj>> _Pr = ranges::less>
_NODISCARD constexpr const _Ty& operator()(
const _Ty& _Val, const _Ty& _Lo, const _Ty& _Hi, _Pr _Pred = {}, _Pj _Proj = {}) const {
_STL_ASSERT(!_STD invoke(_Pred, _STD invoke(_Proj, _Hi), _STD invoke(_Proj, _Lo)),
"The lower bound cannot be greater than the upper bound in a call to std::ranges::clamp "
"(N4861 [alg.clamp]/2).");
if (_STD invoke(_Pred, _STD invoke(_Proj, _Val), _STD invoke(_Proj, _Lo))) {
return _Lo;
}
if (_STD invoke(_Pred, _STD invoke(_Proj, _Hi), _STD invoke(_Proj, _Val))) {
return _Hi;
}
return _Val;
}
};
inline constexpr _Clamp_fn clamp{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17
_STD_END

Просмотреть файл

@ -990,6 +990,17 @@ concept indirectly_movable_storable = indirectly_movable<_In, _Out>
// CONCEPT indirectly_copyable
template <class _In, class _Out>
concept indirectly_copyable = indirectly_readable<_In> && indirectly_writable<_Out, iter_reference_t<_In>>;
// CONCEPT indirectly_copyable_storable
template <class _In, class _Out>
concept indirectly_copyable_storable = indirectly_copyable<_In, _Out>
&& indirectly_writable<_Out, iter_value_t<_In>&>
&& indirectly_writable<_Out, const iter_value_t<_In>&>
&& indirectly_writable<_Out, iter_value_t<_In>&&>
&& indirectly_writable<_Out, const iter_value_t<_In>&&>
&& copyable<iter_value_t<_In>>
&& constructible_from<iter_value_t<_In>, iter_reference_t<_In>>
&& assignable_from<iter_value_t<_In>&, iter_reference_t<_In>>;
// clang-format on
// CUSTOMIZATION POINT OBJECT iter_swap

Просмотреть файл

@ -253,6 +253,7 @@ tests\P0896R4_ranges_alg_for_each
tests\P0896R4_ranges_alg_for_each_n
tests\P0896R4_ranges_alg_is_permutation
tests\P0896R4_ranges_alg_is_sorted
tests\P0896R4_ranges_alg_minmax
tests\P0896R4_ranges_alg_mismatch
tests\P0896R4_ranges_alg_move
tests\P0896R4_ranges_alg_none_of

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\concepts_matrix.lst

Просмотреть файл

@ -0,0 +1,352 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Covers ranges::min, ranges::max, ranges::minmax, ranges::clamp, ranges::min_element, ranges::max_element, and
// ranges::minmax_element
#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>
#include <range_algorithm_support.hpp>
using namespace std;
#define ASSERT(...) assert((__VA_ARGS__))
// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::min_element(borrowed<false>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::min_element(borrowed<true>{})), int*>);
STATIC_ASSERT(same_as<decltype(ranges::max_element(borrowed<false>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::max_element(borrowed<true>{})), int*>);
STATIC_ASSERT(
same_as<decltype(ranges::minmax_element(borrowed<false>{})), ranges::minmax_element_result<ranges::dangling>>);
STATIC_ASSERT(same_as<decltype(ranges::minmax_element(borrowed<true>{})), ranges::minmax_element_result<int*>>);
// Validate that minmax_result and minmax_element_result alias min_max_result
STATIC_ASSERT(same_as<ranges::minmax_result<int>, ranges::min_max_result<int>>);
STATIC_ASSERT(same_as<ranges::minmax_element_result<int>, ranges::min_max_result<int>>);
using P = pair<int, int>;
struct mm_element_empty {
template <ranges::forward_range Fwd>
static constexpr void call() {
// Validate empty ranges
const Fwd range{};
ASSERT(ranges::min_element(range, ranges::less{}, get_first) == ranges::end(range));
ASSERT(ranges::min_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first)
== ranges::end(range));
ASSERT(ranges::max_element(range, ranges::less{}, get_first) == ranges::end(range));
ASSERT(ranges::max_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first)
== ranges::end(range));
{
auto result = ranges::minmax_element(range, ranges::less{}, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::minmax_element_result<ranges::iterator_t<Fwd>>>);
ASSERT(result.min == ranges::end(range));
ASSERT(result.max == ranges::end(range));
}
{
auto result = ranges::minmax_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::minmax_element_result<ranges::iterator_t<Fwd>>>);
ASSERT(result.min == ranges::end(range));
ASSERT(result.max == ranges::end(range));
}
}
};
static constexpr array<P, 8> pairs = {P{5, 0}, P{5, 1}, P{5, 2}, P{5, 3}, P{5, 4}, P{5, 5}, P{5, 6}, P{5, 7}};
struct mm_element {
template <ranges::forward_range Fwd>
static constexpr void call() {
constexpr auto N = pairs.size();
auto elements = pairs;
const Fwd range{elements};
ASSERT(*ranges::min_element(range, ranges::less{}, get_first) == P{5, 0});
ASSERT(*ranges::min_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first) == P{5, 0});
ASSERT(*ranges::max_element(range, ranges::less{}, get_first) == P{5, 0});
ASSERT(*ranges::max_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first) == P{5, 0});
{
auto result = ranges::minmax_element(range, ranges::less{}, get_first);
ASSERT(*result.min == P{5, 0});
ASSERT(*result.max == P{5, 7});
}
{
auto result = ranges::minmax_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first);
ASSERT(*result.min == P{5, 0});
ASSERT(*result.max == P{5, 7});
}
for (size_t i = 0; i < N; ++i) {
elements[i] = P{0, 42};
ASSERT(*ranges::min_element(range, ranges::less{}, get_first) == P{0, 42});
ASSERT(
*ranges::min_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first) == P{0, 42});
ASSERT(*ranges::max_element(range, ranges::less{}, get_first) == (i == 0 ? P{5, 1} : P{5, 0}));
ASSERT(*ranges::max_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first)
== (i == 0 ? P{5, 1} : P{5, 0}));
{
const auto result = ranges::minmax_element(range, ranges::less{}, get_first);
ASSERT(*result.min == P{0, 42});
ASSERT(*result.max == (i == 7 ? P{5, 6} : P{5, 7}));
}
{
const auto result =
ranges::minmax_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first);
ASSERT(*result.min == P{0, 42});
ASSERT(*result.max == (i == 7 ? P{5, 6} : P{5, 7}));
}
for (size_t j = i + 1; j < N; ++j) {
elements[j] = P{0, 13};
ASSERT(*ranges::min_element(range, ranges::less{}, get_first) == P{0, 42});
ASSERT(*ranges::min_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first)
== P{0, 42});
ASSERT(*ranges::max_element(range, ranges::less{}, get_first)
== (i == 0 ? (j == 1 ? P{5, 2} : P{5, 1}) : P{5, 0}));
ASSERT(*ranges::max_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first)
== (i == 0 ? (j == 1 ? P{5, 2} : P{5, 1}) : P{5, 0}));
{
const auto result = ranges::minmax_element(range, ranges::less{}, get_first);
ASSERT(*result.min == P{0, 42});
ASSERT(*result.max == (j == 7 ? (i == 6 ? P{5, 5} : P{5, 6}) : P{5, 7}));
}
{
const auto result =
ranges::minmax_element(ranges::begin(range), ranges::end(range), ranges::less{}, get_first);
ASSERT(*result.min == P{0, 42});
ASSERT(*result.max == (j == 7 ? (i == 6 ? P{5, 5} : P{5, 6}) : P{5, 7}));
}
elements[j] = P{5, static_cast<int>(j)};
}
elements[i] = P{5, static_cast<int>(i)};
}
}
};
struct mm {
template <ranges::input_range In>
static constexpr void call() {
constexpr auto N = pairs.size();
auto elements = pairs;
ASSERT(ranges::min(In{elements}, ranges::less{}, get_first) == P{5, 0});
ASSERT(ranges::max(In{elements}, ranges::less{}, get_first) == P{5, 0});
{
auto result = ranges::minmax(In{elements}, ranges::less{}, get_first);
ASSERT(result.min == P{5, 0});
ASSERT(result.max == P{5, 7});
}
for (size_t i = 0; i < N; ++i) {
elements[i] = P{0, 42};
ASSERT(ranges::min(In{elements}, ranges::less{}, get_first) == P{0, 42});
ASSERT(ranges::max(In{elements}, ranges::less{}, get_first) == (i == 0 ? P{5, 1} : P{5, 0}));
{
auto result = ranges::minmax(In{elements}, ranges::less{}, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::minmax_result<P>>);
ASSERT(result.min == P{0, 42});
ASSERT(result.max == (i == 7 ? P{5, 6} : P{5, 7}));
}
for (size_t j = i + 1; j < N; ++j) {
elements[j] = P{0, 13};
ASSERT(ranges::min(In{elements}, ranges::less{}, get_first) == P{0, 42});
ASSERT(ranges::max(In{elements}, ranges::less{}, get_first)
== (i == 0 ? (j == 1 ? P{5, 2} : P{5, 1}) : P{5, 0}));
{
auto result = ranges::minmax(In{elements}, ranges::less{}, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::minmax_result<P>>);
ASSERT(result.min == P{0, 42});
ASSERT(result.max == (j == 7 ? (i == 6 ? P{5, 5} : P{5, 6}) : P{5, 7}));
}
elements[j] = P{5, static_cast<int>(j)};
}
elements[i] = P{5, static_cast<int>(i)};
}
}
};
constexpr void nonrange_tests() {
// validate overloads of ranges::min, ranges::max, ranges::minmax, and ranges::clamp which take values directly
constexpr auto thirteen = 13;
constexpr auto also_thirteen = thirteen;
constexpr auto forty_two = 42;
constexpr auto thirteen_pair = P{13, 200};
constexpr auto also_thirteen_pair = thirteen_pair;
constexpr auto forty_two_pair = P{42, 100};
ASSERT(&ranges::min(thirteen, forty_two) == &thirteen);
ASSERT(&ranges::min(forty_two, thirteen) == &thirteen);
ASSERT(&ranges::min(thirteen, also_thirteen) == &thirteen);
ASSERT(&ranges::min(thirteen, forty_two, ranges::greater{}) == &forty_two);
ASSERT(&ranges::min(forty_two, thirteen, ranges::greater{}) == &forty_two);
ASSERT(&ranges::min(thirteen, also_thirteen, ranges::greater{}) == &thirteen);
ASSERT(&ranges::min(thirteen_pair, forty_two_pair, ranges::greater{}, get_first) == &forty_two_pair);
ASSERT(&ranges::min(forty_two_pair, thirteen_pair, ranges::greater{}, get_first) == &forty_two_pair);
ASSERT(&ranges::min(thirteen_pair, P{thirteen_pair}, ranges::greater{}, get_first) == &thirteen_pair);
ASSERT(&ranges::max(thirteen, forty_two) == &forty_two);
ASSERT(&ranges::max(forty_two, thirteen) == &forty_two);
ASSERT(&ranges::max(thirteen, also_thirteen) == &thirteen);
ASSERT(&ranges::max(thirteen, forty_two, ranges::greater{}) == &thirteen);
ASSERT(&ranges::max(forty_two, thirteen, ranges::greater{}) == &thirteen);
ASSERT(&ranges::max(thirteen, also_thirteen, ranges::greater{}) == &thirteen);
ASSERT(&ranges::max(thirteen_pair, forty_two_pair, ranges::greater{}, get_first) == &thirteen_pair);
ASSERT(&ranges::max(forty_two_pair, thirteen_pair, ranges::greater{}, get_first) == &thirteen_pair);
ASSERT(&ranges::max(thirteen_pair, P{thirteen_pair}, ranges::greater{}, get_first) == &thirteen_pair);
ASSERT(&ranges::minmax(thirteen, forty_two).min == &thirteen);
ASSERT(&ranges::minmax(thirteen, forty_two).max == &forty_two);
ASSERT(&ranges::minmax(forty_two, thirteen).min == &thirteen);
ASSERT(&ranges::minmax(forty_two, thirteen).max == &forty_two);
ASSERT(&ranges::minmax(thirteen, also_thirteen).min == &thirteen);
ASSERT(&ranges::minmax(thirteen, also_thirteen).max == &also_thirteen);
ASSERT(&ranges::minmax(thirteen, forty_two, ranges::greater{}).min == &forty_two);
ASSERT(&ranges::minmax(thirteen, forty_two, ranges::greater{}).max == &thirteen);
ASSERT(&ranges::minmax(forty_two, thirteen, ranges::greater{}).min == &forty_two);
ASSERT(&ranges::minmax(forty_two, thirteen, ranges::greater{}).max == &thirteen);
ASSERT(&ranges::minmax(thirteen, also_thirteen, ranges::greater{}).min == &thirteen);
ASSERT(&ranges::minmax(thirteen, also_thirteen, ranges::greater{}).max == &also_thirteen);
ASSERT(&ranges::minmax(thirteen_pair, forty_two_pair, ranges::greater{}, get_first).min == &forty_two_pair);
ASSERT(&ranges::minmax(thirteen_pair, forty_two_pair, ranges::greater{}, get_first).max == &thirteen_pair);
ASSERT(&ranges::minmax(forty_two_pair, thirteen_pair, ranges::greater{}, get_first).min == &forty_two_pair);
ASSERT(&ranges::minmax(forty_two_pair, thirteen_pair, ranges::greater{}, get_first).max == &thirteen_pair);
ASSERT(&ranges::minmax(thirteen_pair, also_thirteen_pair, ranges::greater{}, get_first).min == &thirteen_pair);
ASSERT(&ranges::minmax(thirteen_pair, also_thirteen_pair, ranges::greater{}, get_first).max == &also_thirteen_pair);
constexpr auto also_forty_two = 42;
constexpr auto less_than_thirteen = 11;
constexpr auto between_thirteen_and_forty_two = 37;
constexpr auto greater_than_forty_two = 43;
constexpr auto also_forty_two_pair = P{42, 100};
constexpr auto less_than_thirteen_pair = P{11, 250};
constexpr auto between_thirteen_and_forty_two_pair = P{37, 150};
constexpr auto greater_than_forty_two_pair = P{43, 50};
ASSERT(&ranges::clamp(less_than_thirteen, thirteen, forty_two) == &thirteen);
ASSERT(&ranges::clamp(also_thirteen, thirteen, forty_two) == &also_thirteen);
ASSERT(&ranges::clamp(between_thirteen_and_forty_two, thirteen, forty_two) == &between_thirteen_and_forty_two);
ASSERT(&ranges::clamp(also_forty_two, thirteen, forty_two) == &also_forty_two);
ASSERT(&ranges::clamp(greater_than_forty_two, thirteen, forty_two) == &forty_two);
ASSERT(&ranges::clamp(less_than_thirteen_pair, forty_two_pair, thirteen_pair, ranges::greater{}, get_first)
== &thirteen_pair);
ASSERT(&ranges::clamp(also_thirteen_pair, forty_two_pair, thirteen_pair, ranges::greater{}, get_first)
== &also_thirteen_pair);
ASSERT(
&ranges::clamp(between_thirteen_and_forty_two_pair, forty_two_pair, thirteen_pair, ranges::greater{}, get_first)
== &between_thirteen_and_forty_two_pair);
ASSERT(&ranges::clamp(also_forty_two_pair, forty_two_pair, thirteen_pair, ranges::greater{}, get_first)
== &also_forty_two_pair);
ASSERT(&ranges::clamp(greater_than_forty_two_pair, forty_two_pair, thirteen_pair, ranges::greater{}, get_first)
== &forty_two_pair);
}
constexpr void init_list_constexpr_tests() {
ASSERT(
ranges::min({P{5, 0}, P{5, 1}, P{5, 2}, P{5, 3}, P{5, 4}, P{5, 5}, P{5, 6}, P{5, 7}}, ranges::less{}, get_first)
== P{5, 0});
ASSERT(
ranges::max({P{5, 0}, P{5, 1}, P{5, 2}, P{5, 3}, P{5, 4}, P{5, 5}, P{5, 6}, P{5, 7}}, ranges::less{}, get_first)
== P{5, 0});
auto result = ranges::minmax(
{P{5, 0}, P{5, 1}, P{5, 2}, P{5, 3}, P{5, 4}, P{5, 5}, P{5, 6}, P{5, 7}}, ranges::less{}, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::minmax_result<P>>);
ASSERT(result.min == P{5, 0});
ASSERT(result.max == P{5, 7});
}
constexpr void mm_element_constexpr_tests() {
// (min|max|minmax)_element don't care about size, iterator difference, refinements of forward, commonality,
// _or_ proxy vs. non-proxy reference. Let's take a couple variations of forward, and one
// variation of each stronger category.
using test::range, test::Sized, test::CanDifference, test::Common, test::CanCompare, test::ProxyRef, test::fwd;
using E = const P;
mm_element::call<range<fwd, E, Sized::no, CanDifference::no, Common::no, CanCompare::yes, ProxyRef::yes>>();
mm_element::call<range<fwd, E, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::no>>();
mm_element::call<range<std::bidirectional_iterator_tag, E, Sized::no, CanDifference::no, Common::yes,
CanCompare::yes, ProxyRef::yes>>();
mm_element::call<range<std::random_access_iterator_tag, E, Sized::yes, CanDifference::yes, Common::yes,
CanCompare::yes, ProxyRef::yes>>();
mm_element::call<range<std::contiguous_iterator_tag, E, Sized::yes, CanDifference::yes, Common::yes,
CanCompare::yes, ProxyRef::no>>();
}
constexpr void mm_constexpr_tests() {
// Range overloads of (min|max|minmax) don't care about size, iterator difference, commonality, _or_ proxy vs.
// non-proxy reference. They _do_ distinguish input vs. forward. Let's take a couple variations of input and
// forward, and one variation of each stronger category.
using test::range, test::Sized, test::CanDifference, test::Common, test::CanCompare, test::ProxyRef, test::input,
test::fwd;
using E = const P;
mm::call<range<input, E, Sized::no, CanDifference::no, Common::no, CanCompare::yes, ProxyRef::yes>>();
mm::call<range<input, E, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::no>>();
mm::call<range<fwd, E, Sized::no, CanDifference::no, Common::no, CanCompare::yes, ProxyRef::yes>>();
mm::call<range<fwd, E, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes, ProxyRef::no>>();
mm::call<range<std::bidirectional_iterator_tag, E, Sized::no, CanDifference::no, Common::yes, CanCompare::yes,
ProxyRef::yes>>();
mm::call<range<std::random_access_iterator_tag, E, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes,
ProxyRef::yes>>();
mm::call<range<std::contiguous_iterator_tag, E, Sized::yes, CanDifference::yes, Common::yes, CanCompare::yes,
ProxyRef::no>>();
}
int main() {
STATIC_ASSERT((nonrange_tests(), true));
nonrange_tests();
STATIC_ASSERT((init_list_constexpr_tests(), true));
init_list_constexpr_tests();
STATIC_ASSERT((test_fwd<mm_element_empty, const P>(), true));
test_fwd<mm_element_empty, const P>();
STATIC_ASSERT((mm_element_constexpr_tests(), true));
test_fwd<mm_element, const P>();
STATIC_ASSERT((mm_constexpr_tests(), true));
test_in<mm, const P>();
}

Просмотреть файл

@ -46,34 +46,35 @@ struct borrowed { // borrowed<true> is a borrowed_range; borrowed<false> is not
template <>
inline constexpr bool std::ranges::enable_borrowed_range<borrowed<true>> = true;
template <class T>
struct simple_reference {
operator int() const;
operator T() const;
};
template <class T>
struct simple_common_reference {
simple_common_reference(simple_reference);
simple_common_reference(int);
simple_common_reference(simple_reference<T>);
simple_common_reference(T const&);
};
template <int I>
template <int I, class T = int>
struct simple_iter_archetype {
using value_type = int;
using value_type = T;
// 0: not indirectly_readable
simple_reference operator*() const requires(I != 0);
simple_reference<T> operator*() const requires(I != 0); // 0: not indirectly_readable
friend void iter_swap(simple_iter_archetype const&, simple_iter_archetype const&) {}
};
STATIC_ASSERT(!std::indirectly_readable<simple_iter_archetype<0>>);
STATIC_ASSERT(std::indirectly_readable<simple_iter_archetype<1>>);
template <template <class> class TQuals, template <class> class UQuals>
struct std::basic_common_reference<int, ::simple_reference, TQuals, UQuals> {
using type = ::simple_common_reference;
template <class T, template <class> class TQuals, template <class> class UQuals>
struct std::basic_common_reference<T, ::simple_reference<T>, TQuals, UQuals> {
using type = ::simple_common_reference<T>;
};
template <template <class> class TQuals, template <class> class UQuals>
struct std::basic_common_reference<::simple_reference, int, TQuals, UQuals> {
using type = ::simple_common_reference;
template <class T, template <class> class TQuals, template <class> class UQuals>
struct std::basic_common_reference<::simple_reference<T>, T, TQuals, UQuals> {
using type = ::simple_common_reference<T>;
};
namespace indirectly_unary_invocable_test {
@ -94,18 +95,18 @@ namespace indirectly_unary_invocable_test {
// 1: not invocable<Fn&, iter_value_t<simple_iter_archetype>&>
void operator()(int&) const requires(I == 1) = delete;
// 2: not invocable<Fn&, iter_reference_t<simple_iter_archetype>>
void operator()(simple_reference) const requires(I == 2) = delete;
void operator()(simple_reference<int>) const requires(I == 2) = delete;
// 3: not invocable<Fn&, iter_common_reference_t<simple_iter_archetype>>
void operator()(simple_common_reference) const requires(I == 3) = delete;
void operator()(simple_common_reference<int>) const requires(I == 3) = delete;
// 4 : not common_reference_with<invoke_result_t<Fn&, iter_value_t<simple_iter_archetype>&>,
// invoke_result_t<Fn&, iter_reference_t<simple_iter_archetype>>>;
void operator()(int&) const requires(I == 4);
int operator()(simple_reference) const requires(I == 4);
int operator()(simple_reference<int>) const requires(I == 4);
int operator()(int&) const requires(I != 1 && I != 4);
int operator()(simple_reference) const requires(I != 2 && I != 4);
int operator()(simple_common_reference) const requires(I != 3 && I != 4);
int operator()(simple_reference<int>) const requires(I != 2 && I != 4);
int operator()(simple_common_reference<int>) const requires(I != 3 && I != 4);
};
template <class F, class I>
@ -143,13 +144,14 @@ namespace indirect_unary_predicate_test {
// 1: not predicate<Fn&, iter_value_t<simple_iter_archetype>&>
void operator()(int&) const requires(I == 1);
// 2: not predicate<Fn&, iter_reference_t<simple_iter_archetype>>
void operator()(simple_reference) const requires(I == 2) = delete;
void operator()(simple_reference<int>) const requires(I == 2) = delete;
// 3: not predicate<Fn&, iter_common_reference_t<simple_iter_archetype>>
void operator()(simple_common_reference) const requires(I == 3) = delete;
void operator()(simple_common_reference<int>) const requires(I == 3) = delete;
// 4: all of the above
int operator()(int&) const requires(I != 1 && I != 4);
int operator()(simple_reference) const requires(I != 2 && I != 4);
int operator()(simple_common_reference) const requires(I != 3 && I != 4);
int operator()(simple_reference<int>) const requires(I != 2 && I != 4);
int operator()(simple_common_reference<int>) const requires(I != 3 && I != 4);
};
STATIC_ASSERT(!indirect_unary_predicate<Fn<0>, simple_iter_archetype<1>>);
@ -180,19 +182,19 @@ namespace indirect_binary_predicate_test {
// 1: not predicate<Fn&, iter_value_t<simple_iter_archetype>&, iter_value_t<simple_iter_archetype>&>
void operator()(int&, int&) const requires(I == 1);
// 2: not predicate<Fn&, iter_value_t<simple_iter_archetype>&, iter_reference_t<simple_iter_archetype>>
void operator()(int&, simple_reference) const requires(I == 2);
void operator()(int&, simple_reference<int>) const requires(I == 2);
// 3: not predicate<Fn&, iter_reference_t<simple_iter_archetype>, iter_value_t<simple_iter_archetype>&>
void operator()(simple_reference, int&) const requires(I == 3);
void operator()(simple_reference<int>, int&) const requires(I == 3);
// 4: not predicate<Fn&, iter_reference_t<simple_iter_archetype>, iter_reference_t<simple_iter_archetype>>
void operator()(simple_reference, simple_reference) const requires(I == 4);
void operator()(simple_reference<int>, simple_reference<int>) const requires(I == 4);
// 5: not predicate<Fn&, iter_common_reference_t</**/>, iter_common_reference_t</**/>>
void operator()(simple_common_reference, simple_common_reference) const requires(I == 5);
void operator()(simple_common_reference<int>, simple_common_reference<int>) const requires(I == 5);
bool operator()(int&, int&) const requires(I != 1);
int operator()(int&, simple_reference) const requires(I != 2);
int* operator()(simple_reference, int&) const requires(I != 3);
std::true_type operator()(simple_reference, simple_reference) const requires(I != 4);
std::false_type operator()(simple_common_reference, simple_common_reference) const requires(I != 5);
int operator()(int&, simple_reference<int>) const requires(I != 2);
int* operator()(simple_reference<int>, int&) const requires(I != 3);
std::true_type operator()(simple_reference<int>, simple_reference<int>) const requires(I != 4);
std::false_type operator()(simple_common_reference<int>, simple_common_reference<int>) const requires(I != 5);
};
template <int FuncSelector, int IterSelector1, int IterSelector2>
@ -252,19 +254,178 @@ namespace projected_test {
STATIC_ASSERT(test<iter, double (*)(int), double, double>());
} // namespace projected_test
namespace indirectly_copyable_test {
using std::indirectly_copyable;
namespace indirectly_movable_test { // also covers indirectly_movable_storable
using std::assignable_from, std::constructible_from, std::indirectly_writable, std::movable;
using std::indirectly_movable, std::indirectly_movable_storable;
template <int I>
struct value_type {
value_type() = default;
value_type(value_type&&) = default;
value_type& operator=(value_type&&) requires(I != 0) = default; // 0: not movable
// 1: not constructible_from<iter_rvalue_reference_t<In>>:
template <class T>
value_type(simple_reference<T>) requires(I == 1) = delete;
// 2: not assignable_from<iter_rvalue_reference_t<In>>:
template <class T>
value_type& operator=(simple_reference<T>) requires(I != 2);
template <class T>
void operator=(simple_reference<T>) requires(I == 2) = delete;
};
// Ensure specializations of value_type have the intended properties
STATIC_ASSERT(!movable<value_type<0>>);
STATIC_ASSERT(constructible_from<value_type<0>, simple_reference<value_type<0>>>);
STATIC_ASSERT(assignable_from<value_type<0>&, simple_reference<value_type<0>>>);
STATIC_ASSERT(movable<value_type<1>>);
STATIC_ASSERT(!constructible_from<value_type<1>, simple_reference<value_type<1>>>);
STATIC_ASSERT(assignable_from<value_type<1>&, simple_reference<value_type<1>>>);
STATIC_ASSERT(movable<value_type<2>>);
STATIC_ASSERT(constructible_from<value_type<2>, simple_reference<value_type<2>>>);
STATIC_ASSERT(!assignable_from<value_type<2>&, simple_reference<value_type<2>>>);
STATIC_ASSERT(movable<value_type<3>>);
STATIC_ASSERT(constructible_from<value_type<3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(assignable_from<value_type<3>&, simple_reference<value_type<3>>>);
template <int I, int J>
struct out_archetype {
// clang-format off
out_archetype& operator*() const;
// 0: not indirectly_writable<simple_reference>
void operator=(int) const requires(I != 0);
void operator=(simple_reference<value_type<J>>&&) const requires(I == 0) = delete;
void operator=(simple_reference<value_type<J>>&&) const requires(I != 0);
// 1: not indirectly_writable<value_type>
void operator=(value_type<J>&&) const requires(I == 1) = delete;
void operator=(value_type<J>&&) const requires(I != 1);
// clang-format on
};
// Ensure specializations of out_archetype have the intended properties
STATIC_ASSERT(!indirectly_writable<out_archetype<0, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<0, 3>, value_type<3>>);
STATIC_ASSERT(indirectly_writable<out_archetype<1, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(!indirectly_writable<out_archetype<1, 3>, value_type<3>>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, value_type<3>>);
STATIC_ASSERT(!indirectly_copyable<simple_iter_archetype<0>, out_archetype<1>>);
STATIC_ASSERT(!indirectly_copyable<simple_iter_archetype<1>, out_archetype<0>>);
STATIC_ASSERT(indirectly_copyable<simple_iter_archetype<1>, out_archetype<1>>);
// Validate indirectly_movable
STATIC_ASSERT(!indirectly_movable<simple_iter_archetype<0, value_type<3>>, out_archetype<1, 3>>);
STATIC_ASSERT(!indirectly_movable<simple_iter_archetype<1, value_type<3>>, out_archetype<0, 3>>);
STATIC_ASSERT(indirectly_movable<simple_iter_archetype<1, value_type<3>>, out_archetype<1, 3>>);
// Validate indirectly_movable_storable
STATIC_ASSERT(!indirectly_movable_storable<simple_iter_archetype<0, value_type<3>>, out_archetype<2, 3>>);
STATIC_ASSERT(!indirectly_movable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<0, 3>>);
STATIC_ASSERT(!indirectly_movable_storable<simple_iter_archetype<1, value_type<0>>, out_archetype<2, 0>>);
STATIC_ASSERT(!indirectly_movable_storable<simple_iter_archetype<1, value_type<1>>, out_archetype<2, 1>>);
STATIC_ASSERT(!indirectly_movable_storable<simple_iter_archetype<1, value_type<2>>, out_archetype<2, 2>>);
STATIC_ASSERT(indirectly_movable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<2, 3>>);
} // namespace indirectly_movable_test
namespace indirectly_copyable_test { // also covers indirectly_copyable_storable
using std::assignable_from, std::constructible_from, std::copyable, std::indirectly_writable;
using std::indirectly_copyable, std::indirectly_copyable_storable;
template <int I>
struct value_type {
value_type() = default;
value_type(value_type const&) = default;
value_type& operator=(value_type const&) requires(I != 0) = default; // 0: not copyable
// 1: not constructible_from<iter_reference_t<In>>:
template <class T>
value_type(simple_reference<T>) requires(I == 1) = delete;
// 2: not assignable_from<iter_reference_t<In>>:
template <class T>
value_type& operator=(simple_reference<T>) requires(I != 2);
template <class T>
void operator=(simple_reference<T>) requires(I == 2) = delete;
};
// Ensure specializations of value_type have the intended properties
STATIC_ASSERT(!copyable<value_type<0>>);
STATIC_ASSERT(constructible_from<value_type<0>, simple_reference<value_type<0>>>);
STATIC_ASSERT(assignable_from<value_type<0>&, simple_reference<value_type<0>>>);
STATIC_ASSERT(copyable<value_type<1>>);
STATIC_ASSERT(!constructible_from<value_type<1>, simple_reference<value_type<1>>>);
STATIC_ASSERT(assignable_from<value_type<1>&, simple_reference<value_type<1>>>);
STATIC_ASSERT(copyable<value_type<2>>);
STATIC_ASSERT(constructible_from<value_type<2>, simple_reference<value_type<2>>>);
STATIC_ASSERT(!assignable_from<value_type<2>&, simple_reference<value_type<2>>>);
STATIC_ASSERT(copyable<value_type<3>>);
STATIC_ASSERT(constructible_from<value_type<3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(assignable_from<value_type<3>&, simple_reference<value_type<3>>>);
template <int I, int J>
struct out_archetype {
// clang-format off
out_archetype& operator*() const;
// 0: not indirectly_writable<simple_reference>
void operator=(simple_reference<value_type<J>>&&) const requires(I == 0) = delete;
void operator=(simple_reference<value_type<J>>&&) const requires(I != 0);
// 1: not indirectly_writable<value_type&>
void operator=(value_type<J>&) const requires(I == 1) = delete;
void operator=(value_type<J>&) const requires(I != 1);
// 2: not indirectly_writable<value_type&&>
void operator=(value_type<J>&&) const requires(I == 2) = delete;
void operator=(value_type<J>&&) const requires(I != 2);
// 3: not indirectly_writable<const value_type&&>
void operator=(value_type<J> const&&) const requires(I == 3) = delete;
void operator=(value_type<J> const&&) const requires(I != 3);
// 4: not indirectly_writable<const value_type&>
void operator=(value_type<J> const&) const requires(I == 4) = delete;
void operator=(value_type<J> const&) const requires(I != 4);
// clang-format on
};
// Ensure specializations of out_archetype have the intended properties
STATIC_ASSERT(!indirectly_writable<out_archetype<0, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<0, 3>, value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<0, 3>, value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<0, 3>, const value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<0, 3>, const value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<1, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(!indirectly_writable<out_archetype<1, 3>, value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<1, 3>, value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<1, 3>, const value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<1, 3>, const value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, value_type<3>&>);
STATIC_ASSERT(!indirectly_writable<out_archetype<2, 3>, value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, const value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<2, 3>, const value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<3, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<3, 3>, value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<3, 3>, value_type<3>&&>);
STATIC_ASSERT(!indirectly_writable<out_archetype<3, 3>, const value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<3, 3>, const value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<4, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<4, 3>, value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<4, 3>, value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<4, 3>, const value_type<3>&&>);
STATIC_ASSERT(!indirectly_writable<out_archetype<4, 3>, const value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<5, 3>, simple_reference<value_type<3>>>);
STATIC_ASSERT(indirectly_writable<out_archetype<5, 3>, value_type<3>&>);
STATIC_ASSERT(indirectly_writable<out_archetype<5, 3>, value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<5, 3>, const value_type<3>&&>);
STATIC_ASSERT(indirectly_writable<out_archetype<5, 3>, const value_type<3>&>);
// Validate indirectly_copyable
STATIC_ASSERT(!indirectly_copyable<simple_iter_archetype<0, value_type<3>>, out_archetype<1, 3>>);
STATIC_ASSERT(!indirectly_copyable<simple_iter_archetype<1, value_type<3>>, out_archetype<0, 3>>);
STATIC_ASSERT(indirectly_copyable<simple_iter_archetype<1, value_type<3>>, out_archetype<1, 3>>);
// Validate indirectly_copyable_storable
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<0, value_type<3>>, out_archetype<5, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<0, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<1, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<2, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<3, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<4, 3>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<0>>, out_archetype<5, 0>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<1>>, out_archetype<5, 1>>);
STATIC_ASSERT(!indirectly_copyable_storable<simple_iter_archetype<1, value_type<2>>, out_archetype<5, 2>>);
STATIC_ASSERT(indirectly_copyable_storable<simple_iter_archetype<1, value_type<3>>, out_archetype<5, 3>>);
} // namespace indirectly_copyable_test
namespace indirectly_swappable_test {
@ -328,8 +489,8 @@ namespace indirectly_comparable_test {
// Also validate indirect_result_t
using std::indirectly_comparable;
using Proj = int (&)(simple_common_reference);
using BadProj = char const* (&) (simple_common_reference);
using Proj = int (&)(simple_common_reference<int>);
using BadProj = char const* (&) (simple_common_reference<int>);
template <int>
struct base {};