зеркало из https://github.com/microsoft/STL.git
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:
Родитель
021af3e68e
Коммит
c114d51fd1
|
@ -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
|
// not parallelized at present, parallelism expected to be feasible in a future release
|
||||||
return _STD max_element(_First, _Last);
|
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
|
#endif // _HAS_CXX17
|
||||||
|
|
||||||
// FUNCTION TEMPLATE min_element
|
// 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
|
// not parallelized at present, parallelism expected to be feasible in a future release
|
||||||
return _STD min_element(_First, _Last);
|
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
|
#endif // _HAS_CXX17
|
||||||
|
|
||||||
// FUNCTION TEMPLATE minmax_element
|
// 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
|
// not parallelized at present, parallelism expected to be feasible in a future release
|
||||||
return _STD minmax_element(_First, _Last);
|
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
|
#endif // _HAS_CXX17
|
||||||
|
|
||||||
// FUNCTION TEMPLATE max (for initializer_list)
|
// FUNCTION TEMPLATE max (for initializer_list)
|
||||||
|
@ -5608,6 +5798,72 @@ _NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist) {
|
||||||
return (_STD max)(_Ilist, less<>());
|
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)
|
// FUNCTION TEMPLATE min (for initializer_list)
|
||||||
template <class _Ty, class _Pr>
|
template <class _Ty, class _Pr>
|
||||||
_NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist, _Pr _Pred) {
|
_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<>());
|
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
|
// FUNCTION TEMPLATE minmax
|
||||||
template <class _Ty, class _Pr>
|
template <class _Ty, class _Pr>
|
||||||
_NODISCARD constexpr pair<const _Ty&, const _Ty&> minmax(const _Ty& _Left, const _Ty& _Right, _Pr _Pred) noexcept(
|
_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<>());
|
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
|
// FUNCTION TEMPLATE next_permutation
|
||||||
template <class _BidIt, class _Pr>
|
template <class _BidIt, class _Pr>
|
||||||
_CONSTEXPR20 bool next_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) {
|
_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>
|
template <class _Ty>
|
||||||
_NODISCARD constexpr const _Ty& clamp(const _Ty& _Val, const _Ty& _Min_val, const _Ty& _Max_val) {
|
_NODISCARD constexpr const _Ty& clamp(const _Ty& _Val, const _Ty& _Min_val, const _Ty& _Max_val) {
|
||||||
// returns _Val constrained to [_Min_val, _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
|
#endif // _HAS_CXX17
|
||||||
|
|
||||||
_STD_END
|
_STD_END
|
||||||
|
|
|
@ -990,6 +990,17 @@ concept indirectly_movable_storable = indirectly_movable<_In, _Out>
|
||||||
// CONCEPT indirectly_copyable
|
// CONCEPT indirectly_copyable
|
||||||
template <class _In, class _Out>
|
template <class _In, class _Out>
|
||||||
concept indirectly_copyable = indirectly_readable<_In> && indirectly_writable<_Out, iter_reference_t<_In>>;
|
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
|
// clang-format on
|
||||||
|
|
||||||
// CUSTOMIZATION POINT OBJECT iter_swap
|
// 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_for_each_n
|
||||||
tests\P0896R4_ranges_alg_is_permutation
|
tests\P0896R4_ranges_alg_is_permutation
|
||||||
tests\P0896R4_ranges_alg_is_sorted
|
tests\P0896R4_ranges_alg_is_sorted
|
||||||
|
tests\P0896R4_ranges_alg_minmax
|
||||||
tests\P0896R4_ranges_alg_mismatch
|
tests\P0896R4_ranges_alg_mismatch
|
||||||
tests\P0896R4_ranges_alg_move
|
tests\P0896R4_ranges_alg_move
|
||||||
tests\P0896R4_ranges_alg_none_of
|
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 <>
|
template <>
|
||||||
inline constexpr bool std::ranges::enable_borrowed_range<borrowed<true>> = true;
|
inline constexpr bool std::ranges::enable_borrowed_range<borrowed<true>> = true;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
struct simple_reference {
|
struct simple_reference {
|
||||||
operator int() const;
|
operator T() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
struct simple_common_reference {
|
struct simple_common_reference {
|
||||||
simple_common_reference(simple_reference);
|
simple_common_reference(simple_reference<T>);
|
||||||
simple_common_reference(int);
|
simple_common_reference(T const&);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int I>
|
template <int I, class T = int>
|
||||||
struct simple_iter_archetype {
|
struct simple_iter_archetype {
|
||||||
using value_type = int;
|
using value_type = T;
|
||||||
|
|
||||||
// 0: not indirectly_readable
|
simple_reference<T> operator*() const requires(I != 0); // 0: not indirectly_readable
|
||||||
simple_reference operator*() const requires(I != 0);
|
|
||||||
|
|
||||||
friend void iter_swap(simple_iter_archetype const&, simple_iter_archetype const&) {}
|
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<0>>);
|
||||||
STATIC_ASSERT(std::indirectly_readable<simple_iter_archetype<1>>);
|
STATIC_ASSERT(std::indirectly_readable<simple_iter_archetype<1>>);
|
||||||
|
|
||||||
template <template <class> class TQuals, template <class> class UQuals>
|
template <class T, template <class> class TQuals, template <class> class UQuals>
|
||||||
struct std::basic_common_reference<int, ::simple_reference, TQuals, UQuals> {
|
struct std::basic_common_reference<T, ::simple_reference<T>, TQuals, UQuals> {
|
||||||
using type = ::simple_common_reference;
|
using type = ::simple_common_reference<T>;
|
||||||
};
|
};
|
||||||
template <template <class> class TQuals, template <class> class UQuals>
|
template <class T, template <class> class TQuals, template <class> class UQuals>
|
||||||
struct std::basic_common_reference<::simple_reference, int, TQuals, UQuals> {
|
struct std::basic_common_reference<::simple_reference<T>, T, TQuals, UQuals> {
|
||||||
using type = ::simple_common_reference;
|
using type = ::simple_common_reference<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace indirectly_unary_invocable_test {
|
namespace indirectly_unary_invocable_test {
|
||||||
|
@ -94,18 +95,18 @@ namespace indirectly_unary_invocable_test {
|
||||||
// 1: not invocable<Fn&, iter_value_t<simple_iter_archetype>&>
|
// 1: not invocable<Fn&, iter_value_t<simple_iter_archetype>&>
|
||||||
void operator()(int&) const requires(I == 1) = delete;
|
void operator()(int&) const requires(I == 1) = delete;
|
||||||
// 2: not invocable<Fn&, iter_reference_t<simple_iter_archetype>>
|
// 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>>
|
// 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>&>,
|
// 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>>>;
|
// invoke_result_t<Fn&, iter_reference_t<simple_iter_archetype>>>;
|
||||||
void operator()(int&) const requires(I == 4);
|
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()(int&) const requires(I != 1 && I != 4);
|
||||||
int operator()(simple_reference) const requires(I != 2 && I != 4);
|
int operator()(simple_reference<int>) const requires(I != 2 && I != 4);
|
||||||
int operator()(simple_common_reference) const requires(I != 3 && I != 4);
|
int operator()(simple_common_reference<int>) const requires(I != 3 && I != 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class F, class I>
|
template <class F, class I>
|
||||||
|
@ -143,13 +144,14 @@ namespace indirect_unary_predicate_test {
|
||||||
// 1: not predicate<Fn&, iter_value_t<simple_iter_archetype>&>
|
// 1: not predicate<Fn&, iter_value_t<simple_iter_archetype>&>
|
||||||
void operator()(int&) const requires(I == 1);
|
void operator()(int&) const requires(I == 1);
|
||||||
// 2: not predicate<Fn&, iter_reference_t<simple_iter_archetype>>
|
// 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>>
|
// 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()(int&) const requires(I != 1 && I != 4);
|
||||||
int operator()(simple_reference) const requires(I != 2 && I != 4);
|
int operator()(simple_reference<int>) const requires(I != 2 && I != 4);
|
||||||
int operator()(simple_common_reference) const requires(I != 3 && 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>>);
|
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>&>
|
// 1: not predicate<Fn&, iter_value_t<simple_iter_archetype>&, iter_value_t<simple_iter_archetype>&>
|
||||||
void operator()(int&, int&) const requires(I == 1);
|
void operator()(int&, int&) const requires(I == 1);
|
||||||
// 2: not predicate<Fn&, iter_value_t<simple_iter_archetype>&, iter_reference_t<simple_iter_archetype>>
|
// 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>&>
|
// 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>>
|
// 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</**/>>
|
// 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);
|
bool operator()(int&, int&) const requires(I != 1);
|
||||||
int operator()(int&, simple_reference) const requires(I != 2);
|
int operator()(int&, simple_reference<int>) const requires(I != 2);
|
||||||
int* operator()(simple_reference, int&) const requires(I != 3);
|
int* operator()(simple_reference<int>, int&) const requires(I != 3);
|
||||||
std::true_type operator()(simple_reference, simple_reference) const requires(I != 4);
|
std::true_type operator()(simple_reference<int>, simple_reference<int>) const requires(I != 4);
|
||||||
std::false_type operator()(simple_common_reference, simple_common_reference) const requires(I != 5);
|
std::false_type operator()(simple_common_reference<int>, simple_common_reference<int>) const requires(I != 5);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int FuncSelector, int IterSelector1, int IterSelector2>
|
template <int FuncSelector, int IterSelector1, int IterSelector2>
|
||||||
|
@ -252,19 +254,178 @@ namespace projected_test {
|
||||||
STATIC_ASSERT(test<iter, double (*)(int), double, double>());
|
STATIC_ASSERT(test<iter, double (*)(int), double, double>());
|
||||||
} // namespace projected_test
|
} // namespace projected_test
|
||||||
|
|
||||||
namespace indirectly_copyable_test {
|
namespace indirectly_movable_test { // also covers indirectly_movable_storable
|
||||||
using std::indirectly_copyable;
|
using std::assignable_from, std::constructible_from, std::indirectly_writable, std::movable;
|
||||||
|
using std::indirectly_movable, std::indirectly_movable_storable;
|
||||||
|
|
||||||
template <int I>
|
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 {
|
struct out_archetype {
|
||||||
|
// clang-format off
|
||||||
out_archetype& operator*() const;
|
out_archetype& operator*() const;
|
||||||
// 0: not indirectly_writable<simple_reference>
|
// 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>>);
|
// Validate indirectly_movable
|
||||||
STATIC_ASSERT(!indirectly_copyable<simple_iter_archetype<1>, out_archetype<0>>);
|
STATIC_ASSERT(!indirectly_movable<simple_iter_archetype<0, value_type<3>>, out_archetype<1, 3>>);
|
||||||
STATIC_ASSERT(indirectly_copyable<simple_iter_archetype<1>, out_archetype<1>>);
|
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_copyable_test
|
||||||
|
|
||||||
namespace indirectly_swappable_test {
|
namespace indirectly_swappable_test {
|
||||||
|
@ -328,8 +489,8 @@ namespace indirectly_comparable_test {
|
||||||
// Also validate indirect_result_t
|
// Also validate indirect_result_t
|
||||||
using std::indirectly_comparable;
|
using std::indirectly_comparable;
|
||||||
|
|
||||||
using Proj = int (&)(simple_common_reference);
|
using Proj = int (&)(simple_common_reference<int>);
|
||||||
using BadProj = char const* (&) (simple_common_reference);
|
using BadProj = char const* (&) (simple_common_reference<int>);
|
||||||
|
|
||||||
template <int>
|
template <int>
|
||||||
struct base {};
|
struct base {};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче