зеркало из https://github.com/microsoft/STL.git
Implement views::take and views::drop (#1253)
Also implements the proposed resolution of LWG-3481.
This commit is contained in:
Родитель
6156ec32d5
Коммит
0c2007a67b
|
@ -534,21 +534,20 @@ public:
|
|||
requires convertible_to<const _Other&, _Iter>
|
||||
constexpr counted_iterator(const counted_iterator<_Other>& _Right) noexcept(
|
||||
is_nothrow_constructible_v<_Iter, const _Other&>) // strengthened
|
||||
: _Current(_Right._Current), _Length(_Right._Length) {}
|
||||
: _Current(_Right.base()), _Length(_Right.count()) {}
|
||||
|
||||
template <class _Other>
|
||||
requires assignable_from<_Iter&, const _Other&>
|
||||
constexpr counted_iterator& operator=(const counted_iterator<_Other>& _Right) noexcept(
|
||||
is_nothrow_assignable_v<_Iter&, const _Other&>) /* strengthened */ {
|
||||
// clang-format on
|
||||
_Current = _Right._Current;
|
||||
_Length = _Right._Length;
|
||||
_Current = _Right.base();
|
||||
_Length = _Right.count();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// [counted.iter.access]
|
||||
_NODISCARD constexpr _Iter base() const& noexcept(is_nothrow_copy_constructible_v<_Iter>) /* strengthened */
|
||||
requires copy_constructible<_Iter> {
|
||||
_NODISCARD constexpr const _Iter& base() const& noexcept /* strengthened */ { // Per LWG-3391
|
||||
return _Current;
|
||||
}
|
||||
|
||||
|
@ -720,8 +719,8 @@ public:
|
|||
|
||||
template <indirectly_swappable<_Iter> _Other>
|
||||
friend constexpr void iter_swap(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) noexcept(
|
||||
noexcept(_RANGES iter_swap(_Left._Current, _Right._Current))) {
|
||||
_RANGES iter_swap(_Left._Current, _Right._Current);
|
||||
noexcept(_RANGES iter_swap(_Left._Current, _Right.base()))) {
|
||||
_RANGES iter_swap(_Left._Current, _Right.base());
|
||||
}
|
||||
|
||||
template <common_with<_Iter> _Other>
|
||||
|
@ -750,7 +749,7 @@ public:
|
|||
template <common_with<_Iter> _Other>
|
||||
friend constexpr void _Verify_range(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) {
|
||||
if constexpr (_Range_verifiable_v<_Iter, _Other>) {
|
||||
_Verify_range(_Left._Current, _Right._Current);
|
||||
_Verify_range(_Left._Current, _Right.base());
|
||||
}
|
||||
#if _ITERATOR_DEBUG_LEVEL != 0
|
||||
_Same_sequence(_Left, _Right);
|
||||
|
@ -784,8 +783,8 @@ public:
|
|||
requires _Wrapped_seekable_v<_Iter, const _Other&>
|
||||
constexpr void _Seek_to(const counted_iterator<_Other>& _It) {
|
||||
// clang-format on
|
||||
_Current._Seek_to(_It._Current);
|
||||
_Length = _It._Length;
|
||||
_Current._Seek_to(_It.base());
|
||||
_Length = _It.count();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
@ -793,14 +792,11 @@ public:
|
|||
requires _Wrapped_seekable_v<_Iter, _Other>
|
||||
constexpr void _Seek_to(counted_iterator<_Other>&& _It) {
|
||||
// clang-format on
|
||||
_Current._Seek_to(_STD move(_It)._Current);
|
||||
_Length = _It._Length;
|
||||
_Current._Seek_to(_STD move(_It).base());
|
||||
_Length = _It.count();
|
||||
}
|
||||
|
||||
private:
|
||||
template <input_or_output_iterator>
|
||||
friend class counted_iterator;
|
||||
|
||||
_Iter _Current{};
|
||||
iter_difference_t<_Iter> _Length = 0;
|
||||
};
|
||||
|
@ -814,6 +810,17 @@ template <input_iterator _Iter>
|
|||
struct iterator_traits<counted_iterator<_Iter>> : iterator_traits<_Iter> {
|
||||
using pointer = void;
|
||||
};
|
||||
|
||||
template <contiguous_iterator _Iter>
|
||||
struct pointer_traits<counted_iterator<_Iter>> { // TRANSITION, address LWG-3408 and include this
|
||||
using pointer = counted_iterator<_Iter>;
|
||||
using element_type = remove_reference_t<iter_reference_t<_Iter>>;
|
||||
using difference_type = iter_difference_t<_Iter>;
|
||||
|
||||
_NODISCARD static constexpr element_type* to_address(const pointer _It) noexcept {
|
||||
return _STD to_address(_It.base());
|
||||
}
|
||||
};
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
_STD_END
|
||||
|
|
423
stl/inc/ranges
423
stl/inc/ranges
|
@ -12,6 +12,9 @@
|
|||
#pragma message("The contents of <ranges> are available only with C++20 concepts support.")
|
||||
#else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv
|
||||
#include <iterator>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#if 1 // TRANSITION, VSO-1174090
|
||||
#include <optional>
|
||||
#endif // TRANSITION, VSO-1174090
|
||||
|
@ -47,6 +50,12 @@ namespace ranges {
|
|||
&& (is_pointer_v<_It> || _Has_member_arrow<_It&>);
|
||||
// clang-format on
|
||||
|
||||
template <bool _IsConst, class _Ty>
|
||||
using _Maybe_const = conditional_t<_IsConst, const _Ty, _Ty>;
|
||||
|
||||
template <bool _IsWrapped, class _Ty>
|
||||
using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>;
|
||||
|
||||
namespace _Pipe {
|
||||
// clang-format off
|
||||
template <class _Left, class _Right>
|
||||
|
@ -999,6 +1008,420 @@ namespace ranges {
|
|||
inline constexpr _Filter_fn filter;
|
||||
} // namespace views
|
||||
|
||||
// CLASS TEMPLATE ranges::take_view
|
||||
template <view _Vw>
|
||||
class take_view : public view_interface<take_view<_Vw>> {
|
||||
private:
|
||||
/* [[no_unique_address]] */ _Vw _Range{};
|
||||
range_difference_t<_Vw> _Count = 0;
|
||||
|
||||
template <bool _Const, bool _Wrapped = true>
|
||||
class _Sentinel {
|
||||
private:
|
||||
template <bool, bool>
|
||||
friend class _Sentinel;
|
||||
|
||||
using _Base_Ty = _Maybe_const<_Const, _Vw>;
|
||||
using _Base_sentinel = _Maybe_wrapped<_Wrapped, sentinel_t<_Base_Ty>>;
|
||||
using _Counted_Iter = counted_iterator<_Maybe_wrapped<_Wrapped, iterator_t<_Base_Ty>>>;
|
||||
|
||||
_Base_sentinel _Last{};
|
||||
|
||||
public:
|
||||
_Sentinel() = default;
|
||||
|
||||
constexpr explicit _Sentinel(_Base_sentinel _Last_) noexcept(
|
||||
is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened
|
||||
: _Last(_STD move(_Last_)) {}
|
||||
|
||||
// clang-format off
|
||||
constexpr _Sentinel(_Sentinel<!_Const, _Wrapped> _That) noexcept(
|
||||
is_nothrow_constructible_v<_Base_sentinel, _Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>>) // strengthened
|
||||
requires _Const && convertible_to<_Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>, _Base_sentinel>
|
||||
: _Last(_STD move(_That._Last)) {}
|
||||
// clang-format on
|
||||
|
||||
_NODISCARD constexpr _Base_sentinel base() const
|
||||
noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ {
|
||||
return _Last;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator==(const _Counted_Iter& _Left, const _Sentinel& _Right) {
|
||||
return _Left.count() == 0 || _Left.base() == _Right._Last;
|
||||
}
|
||||
|
||||
using _Prevent_inheriting_unwrap = _Sentinel;
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto _Unwrapped() const&
|
||||
requires _Wrapped && _Unwrappable_v<const iterator_t<_Base_Ty>&> {
|
||||
// clang-format on
|
||||
return _Sentinel<_Const, false>{_Get_unwrapped(_Last)};
|
||||
}
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v<iterator_t<_Base_Ty>> {
|
||||
// clang-format on
|
||||
return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))};
|
||||
}
|
||||
|
||||
static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<iterator_t<_Base_Ty>>;
|
||||
|
||||
constexpr void _Seek_to(const _Sentinel<_Const, false>& _That) requires _Wrapped {
|
||||
_Seek_wrapped(_Last, _That._Last);
|
||||
}
|
||||
constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped {
|
||||
_Seek_wrapped(_Last, _STD move(_That._Last));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
take_view() = default;
|
||||
|
||||
constexpr take_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept(
|
||||
is_nothrow_move_constructible_v<_Vw>) // strengthened
|
||||
: _Range(_STD move(_Range_)), _Count{_Count_} {}
|
||||
|
||||
_NODISCARD constexpr _Vw base() const& noexcept(
|
||||
is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> {
|
||||
return _Range;
|
||||
}
|
||||
_NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ {
|
||||
return _STD move(_Range);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto begin() requires (!_Simple_view<_Vw>) {
|
||||
// clang-format on
|
||||
if constexpr (sized_range<_Vw>) {
|
||||
if constexpr (random_access_range<_Vw>) {
|
||||
return _RANGES begin(_Range);
|
||||
} else {
|
||||
const auto _Size = size();
|
||||
return counted_iterator{_RANGES begin(_Range), _Size};
|
||||
}
|
||||
} else {
|
||||
return counted_iterator{_RANGES begin(_Range), _Count};
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto begin() const requires range<const _Vw> {
|
||||
if constexpr (sized_range<const _Vw>) {
|
||||
if constexpr (random_access_range<const _Vw>) {
|
||||
return _RANGES begin(_Range);
|
||||
} else {
|
||||
const auto _Size = size();
|
||||
return counted_iterator{_RANGES begin(_Range), _Size};
|
||||
}
|
||||
} else {
|
||||
return counted_iterator{_RANGES begin(_Range), _Count};
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) {
|
||||
// clang-format on
|
||||
if constexpr (sized_range<_Vw>) {
|
||||
if constexpr (random_access_range<_Vw>) {
|
||||
return _RANGES begin(_Range) + static_cast<range_difference_t<_Vw>>(size());
|
||||
} else {
|
||||
return default_sentinel;
|
||||
}
|
||||
} else {
|
||||
return _Sentinel<false>{_RANGES end(_Range)};
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto end() const requires range<const _Vw> {
|
||||
if constexpr (sized_range<const _Vw>) {
|
||||
if constexpr (random_access_range<const _Vw>) {
|
||||
return _RANGES begin(_Range) + static_cast<range_difference_t<_Vw>>(size());
|
||||
} else {
|
||||
return default_sentinel;
|
||||
}
|
||||
} else {
|
||||
return _Sentinel<true>{_RANGES end(_Range)};
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto size() requires sized_range<_Vw> {
|
||||
const auto _Length = _RANGES size(_Range);
|
||||
return (_STD min)(_Length, static_cast<decltype(_Length)>(_Count));
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto size() const requires sized_range<const _Vw> {
|
||||
const auto _Length = _RANGES size(_Range);
|
||||
return (_STD min)(_Length, static_cast<decltype(_Length)>(_Count));
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rng> // Per P/R of LWG-3447
|
||||
take_view(_Rng&&, range_difference_t<_Rng>)->take_view<views::all_t<_Rng>>;
|
||||
|
||||
namespace views {
|
||||
// VARIABLE views::take
|
||||
template <class>
|
||||
static constexpr bool _Is_dynamic_span = false;
|
||||
template <class _Elem>
|
||||
static constexpr bool _Is_dynamic_span<span<_Elem, dynamic_extent>> = true;
|
||||
|
||||
template <class>
|
||||
static constexpr bool _Is_subrange = false;
|
||||
template <class _It, class _Se, subrange_kind _Ki>
|
||||
static constexpr bool _Is_subrange<subrange<_It, _Se, _Ki>> = true;
|
||||
|
||||
template <class _Rng>
|
||||
concept _Reconstructible_range = random_access_range<_Rng> && sized_range<_Rng>
|
||||
&& (_Is_dynamic_span<remove_cvref_t<_Rng>>
|
||||
// || _Is_specialization_v<remove_cvref_t<_Rng>, basic_string_view> // TRANSITION, P1391R4
|
||||
// || _Is_specialization_v<remove_cvref_t<_Rng>, iota_view> // TRANSITION, iota_view
|
||||
|| _Is_subrange<remove_cvref_t<_Rng>>);
|
||||
|
||||
class _Take_fn {
|
||||
private:
|
||||
enum class _St { _Empty, _Preserve, _Take_view };
|
||||
|
||||
template <class _Rng>
|
||||
_NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept {
|
||||
using _Ty = remove_cvref_t<_Rng>;
|
||||
|
||||
if constexpr (_Is_specialization_v<_Ty, empty_view>) {
|
||||
return {_St::_Empty, true};
|
||||
} else if constexpr (_Reconstructible_range<_Rng>) {
|
||||
return {_St::_Preserve,
|
||||
noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()),
|
||||
_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>())})};
|
||||
} else {
|
||||
return {_St::_Take_view, noexcept(take_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})};
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Rng>
|
||||
static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>();
|
||||
|
||||
template <_Integer_like _Ty>
|
||||
struct _Partial : _Pipe::_Base<_Partial<_Ty>> {
|
||||
_Ty _Length;
|
||||
|
||||
// clang-format off
|
||||
template <viewable_range _Rng>
|
||||
requires convertible_to<_Ty&, range_difference_t<_Rng>>
|
||||
_NODISCARD constexpr auto operator()(_Rng&& _Range) const
|
||||
noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _Length))) {
|
||||
// clang-format on
|
||||
_STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>);
|
||||
return _Take_fn{}(_STD forward<_Rng>(_Range), _Length);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// clang-format off
|
||||
template <viewable_range _Rng>
|
||||
_NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept(
|
||||
_Choice<_Rng>._No_throw) {
|
||||
// clang-format on
|
||||
if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) {
|
||||
// it's an empty_view: return another empty view
|
||||
return remove_cvref_t<_Rng>{};
|
||||
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) {
|
||||
// it's a "reconstructible range"; return the same kind of range with a restricted extent
|
||||
_Count = (_STD min)(_RANGES distance(_Range), _Count);
|
||||
const auto _First = _RANGES begin(_Range);
|
||||
return remove_cvref_t<_Rng>{_First, _First + _Count};
|
||||
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Take_view) {
|
||||
return take_view{_STD forward<_Rng>(_Range), _Count};
|
||||
}
|
||||
}
|
||||
|
||||
template <_Integer_like _Ty>
|
||||
_NODISCARD constexpr auto operator()(_Ty _Length) const noexcept {
|
||||
return _Partial<_Ty>{._Length = _Length};
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Take_fn take;
|
||||
} // namespace views
|
||||
|
||||
// CLASS TEMPLATE ranges::drop_view
|
||||
template <view _Vw>
|
||||
class drop_view : public _Cached_position_t<forward_range<_Vw> && !(random_access_range<_Vw> && sized_range<_Vw>),
|
||||
_Vw, drop_view<_Vw>> {
|
||||
private:
|
||||
/* [[no_unique_address]] */ _Vw _Range{};
|
||||
range_difference_t<_Vw> _Count = 0;
|
||||
|
||||
public:
|
||||
drop_view() = default;
|
||||
|
||||
constexpr drop_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept(
|
||||
is_nothrow_move_constructible_v<_Vw>) // strengthened
|
||||
: _Range(_STD move(_Range_)), _Count{_Count_} {
|
||||
#if _CONTAINER_DEBUG_LEVEL > 0
|
||||
_STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4861 [range.drop.view]/1");
|
||||
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
||||
}
|
||||
|
||||
_NODISCARD constexpr _Vw base() const& noexcept(
|
||||
is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> {
|
||||
return _Range;
|
||||
}
|
||||
_NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ {
|
||||
return _STD move(_Range);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto begin() // constraints per proposed resolution of LWG-3482
|
||||
requires (!(_Simple_view<_Vw> && random_access_range<const _Vw> && sized_range<const _Vw>)) {
|
||||
// clang-format on
|
||||
if constexpr (sized_range<_Vw> && random_access_range<_Vw>) {
|
||||
const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count);
|
||||
return _RANGES begin(_Range) + _Offset;
|
||||
} else {
|
||||
if constexpr (forward_range<_Vw>) {
|
||||
if (this->_Has_cache()) {
|
||||
return this->_Get_cache(_Range);
|
||||
}
|
||||
}
|
||||
|
||||
iterator_t<_Vw> _Result;
|
||||
if constexpr (sized_range<_Vw>) {
|
||||
auto _Offset = _RANGES distance(_Range);
|
||||
if constexpr (bidirectional_range<_Vw> && common_range<_Vw>) {
|
||||
if (_Count >= _Offset / 2) {
|
||||
_Result = _RANGES end(_Range);
|
||||
while (_Offset > _Count) {
|
||||
--_Offset;
|
||||
--_Result;
|
||||
}
|
||||
|
||||
this->_Set_cache(_Range, _Result);
|
||||
return _Result;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Offset > _Count) {
|
||||
_Offset = _Count;
|
||||
}
|
||||
|
||||
_Result = _RANGES next(_RANGES begin(_Range), _Offset);
|
||||
} else {
|
||||
_Result = _RANGES next(_RANGES begin(_Range), _Count, _RANGES end(_Range));
|
||||
}
|
||||
|
||||
if constexpr (forward_range<_Vw>) {
|
||||
this->_Set_cache(_Range, _Result);
|
||||
}
|
||||
return _Result;
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto begin() const // constraints per proposed resolution of LWG-3482
|
||||
requires random_access_range<const _Vw> && sized_range<const _Vw> {
|
||||
// clang-format on
|
||||
const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count);
|
||||
return _RANGES begin(_Range) + _Offset;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
_NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) {
|
||||
// clang-format on
|
||||
return _RANGES end(_Range);
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto end() const requires range<const _Vw> {
|
||||
return _RANGES end(_Range);
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto size() requires sized_range<_Vw> {
|
||||
const auto _Size = _RANGES size(_Range);
|
||||
const auto _Count_as_size = static_cast<range_size_t<_Vw>>(_Count);
|
||||
if (_Size < _Count_as_size) {
|
||||
return range_size_t<_Vw>{0};
|
||||
} else {
|
||||
return static_cast<range_size_t<_Vw>>(_Size - _Count_as_size);
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD constexpr auto size() const requires sized_range<const _Vw> {
|
||||
const auto _Size = _RANGES size(_Range);
|
||||
const auto _Count_as_size = static_cast<range_size_t<_Vw>>(_Count);
|
||||
if (_Size < _Count_as_size) {
|
||||
return range_size_t<_Vw>{0};
|
||||
} else {
|
||||
return static_cast<range_size_t<_Vw>>(_Size - _Count_as_size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rng>
|
||||
drop_view(_Rng&&, range_difference_t<_Rng>) -> drop_view<views::all_t<_Rng>>;
|
||||
|
||||
namespace views {
|
||||
// VARIABLE views::drop
|
||||
class _Drop_fn {
|
||||
private:
|
||||
enum class _St { _Empty, _Preserve, _Drop_view };
|
||||
|
||||
template <class _Rng>
|
||||
_NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept {
|
||||
using _Ty = remove_cvref_t<_Rng>;
|
||||
|
||||
if constexpr (_Is_specialization_v<_Ty, empty_view>) {
|
||||
return {_St::_Empty, true};
|
||||
} else if constexpr (_Reconstructible_range<_Rng>) {
|
||||
return {_St::_Preserve,
|
||||
noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()),
|
||||
_RANGES end(_STD declval<_Rng&>())})};
|
||||
} else {
|
||||
return {_St::_Drop_view, noexcept(drop_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})};
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Rng>
|
||||
static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>();
|
||||
|
||||
template <_Integer_like _Ty>
|
||||
struct _Partial : _Pipe::_Base<_Partial<_Ty>> {
|
||||
_Ty _Length;
|
||||
|
||||
// clang-format off
|
||||
template <viewable_range _Rng>
|
||||
requires convertible_to<_Ty&, range_difference_t<_Rng>>
|
||||
_NODISCARD constexpr auto operator()(_Rng&& _Range) const
|
||||
noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _Length))) {
|
||||
// clang-format on
|
||||
_STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>);
|
||||
return _Drop_fn{}(_STD forward<_Rng>(_Range), _Length);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// clang-format off
|
||||
template <viewable_range _Rng>
|
||||
_NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept(
|
||||
_Choice<_Rng>._No_throw) {
|
||||
// clang-format on
|
||||
if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) {
|
||||
// it's an empty_view: return another empty view
|
||||
return remove_cvref_t<_Rng>{};
|
||||
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) {
|
||||
// it's a "reconstructible range"; return the same kind of range with a restricted extent
|
||||
_Count = (_STD min)(_RANGES distance(_Range), _Count);
|
||||
return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)};
|
||||
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Drop_view) {
|
||||
return drop_view{_STD forward<_Rng>(_Range), _Count};
|
||||
}
|
||||
}
|
||||
|
||||
template <_Integer_like _Ty>
|
||||
_NODISCARD constexpr auto operator()(_Ty _Length) const noexcept {
|
||||
return _Partial<_Ty>{._Length = _Length};
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Drop_fn drop;
|
||||
} // namespace views
|
||||
|
||||
// CLASS TEMPLATE ranges::reverse_view
|
||||
// clang-format off
|
||||
template <view _Vw>
|
||||
|
|
|
@ -3257,7 +3257,8 @@ namespace ranges {
|
|||
using _Not_quite_object::_Not_quite_object;
|
||||
|
||||
template <input_or_output_iterator _It, sentinel_for<_It> _Se>
|
||||
_NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const {
|
||||
_NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const
|
||||
noexcept(_Nothrow_distance<_It, _Se>) /* strengthened */ {
|
||||
if constexpr (sized_sentinel_for<_Se, _It>) {
|
||||
return _Last - _First;
|
||||
} else {
|
||||
|
@ -3267,7 +3268,8 @@ namespace ranges {
|
|||
}
|
||||
|
||||
template <range _Rng>
|
||||
_NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const {
|
||||
_NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const
|
||||
noexcept(_Nothrow_size<_Rng>) /* strengthened */ {
|
||||
if constexpr (sized_range<_Rng>) {
|
||||
return static_cast<range_difference_t<_Rng>>(_RANGES size(_Range));
|
||||
} else {
|
||||
|
@ -3277,7 +3279,8 @@ namespace ranges {
|
|||
|
||||
private:
|
||||
template <class _It, class _Se>
|
||||
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) {
|
||||
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept(
|
||||
_Nothrow_distance<_It, _Se>) {
|
||||
_STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
|
||||
|
||||
|
@ -3288,6 +3291,18 @@ namespace ranges {
|
|||
|
||||
return _Count;
|
||||
}
|
||||
|
||||
template <class _It, class _Se>
|
||||
static constexpr bool _Nothrow_distance = noexcept(
|
||||
noexcept(++_STD declval<_Unwrapped_t<_It>&>() != _STD declval<const _Unwrapped_t<_Se>&>()));
|
||||
template <class _It, sized_sentinel_for<_It> _Se>
|
||||
static constexpr bool _Nothrow_distance<_It, _Se> = noexcept(
|
||||
noexcept(_STD declval<_Se&>() - _STD declval<_It&>()));
|
||||
|
||||
template <class _Rng>
|
||||
static constexpr bool _Nothrow_size = _Nothrow_distance<iterator_t<_Rng>, sentinel_t<_Rng>>;
|
||||
template <sized_range _Rng>
|
||||
static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>()));
|
||||
};
|
||||
|
||||
inline constexpr _Distance_fn distance{_Not_quite_object::_Construct_tag{}};
|
||||
|
@ -3991,10 +4006,10 @@ public:
|
|||
// clang-format on
|
||||
|
||||
#ifdef __cpp_lib_concepts
|
||||
_NODISCARD constexpr const iterator_type& base() const& { // Per LWG-3391
|
||||
_NODISCARD constexpr const iterator_type& base() const& noexcept /* strengthened */ { // Per LWG-3391
|
||||
return _Current;
|
||||
}
|
||||
_NODISCARD constexpr iterator_type base() && {
|
||||
_NODISCARD constexpr iterator_type base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ {
|
||||
return _STD move(_Current);
|
||||
}
|
||||
#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv
|
||||
|
|
|
@ -340,6 +340,22 @@ namespace test {
|
|||
static constexpr bool at_least = derived_from<Category, T>;
|
||||
|
||||
using ReferenceType = conditional_t<to_bool(Proxy), proxy_reference<Category, Element>, Element&>;
|
||||
|
||||
struct post_increment_proxy {
|
||||
Element* ptr_;
|
||||
|
||||
const post_increment_proxy& operator*() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::indirectly_writable<Element*, T>
|
||||
const post_increment_proxy& operator=(T&& t) const noexcept {
|
||||
*ptr_ = std::forward<T>(t);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
using Consterator = iterator<Category, const Element, Diff, Eq, Proxy, Wrapped>;
|
||||
|
||||
|
@ -385,10 +401,11 @@ namespace test {
|
|||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
constexpr iterator operator++(int) & noexcept {
|
||||
auto tmp = *this;
|
||||
|
||||
constexpr post_increment_proxy operator++(int) & noexcept requires std::is_same_v<Category, output> {
|
||||
post_increment_proxy result{ptr_};
|
||||
++ptr_;
|
||||
return tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator--() & {
|
||||
|
@ -428,6 +445,13 @@ namespace test {
|
|||
ranges::iter_swap(x.ptr_, y.ptr_);
|
||||
}
|
||||
|
||||
// forward iterator operations:
|
||||
constexpr iterator operator++(int) & noexcept requires at_least<fwd> {
|
||||
auto tmp = *this;
|
||||
++ptr_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// sentinel operations (implied by forward iterator):
|
||||
iterator(iterator const&) requires (to_bool(Eq)) = default;
|
||||
iterator& operator=(iterator const&) requires (to_bool(Eq)) = default;
|
||||
|
|
|
@ -320,11 +320,13 @@ tests\P0896R4_ranges_subrange
|
|||
tests\P0896R4_ranges_test_machinery
|
||||
tests\P0896R4_ranges_to_address
|
||||
tests\P0896R4_views_all
|
||||
tests\P0896R4_views_drop
|
||||
tests\P0896R4_views_empty
|
||||
tests\P0896R4_views_filter
|
||||
tests\P0896R4_views_filter_death
|
||||
tests\P0896R4_views_reverse
|
||||
tests\P0896R4_views_single
|
||||
tests\P0896R4_views_take
|
||||
tests\P0898R3_concepts
|
||||
tests\P0898R3_identity
|
||||
tests\P0912R5_coroutine
|
||||
|
|
|
@ -90,9 +90,11 @@ STATIC_ASSERT(test_cpo(ranges::data));
|
|||
STATIC_ASSERT(test_cpo(ranges::cdata));
|
||||
|
||||
STATIC_ASSERT(test_cpo(ranges::views::all));
|
||||
STATIC_ASSERT(test_cpo(ranges::views::drop));
|
||||
STATIC_ASSERT(test_cpo(ranges::views::filter));
|
||||
STATIC_ASSERT(test_cpo(ranges::views::reverse));
|
||||
STATIC_ASSERT(test_cpo(ranges::views::single));
|
||||
STATIC_ASSERT(test_cpo(ranges::views::take));
|
||||
|
||||
void test_cpo_ambiguity() {
|
||||
using namespace std::ranges;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\strict_concepts_matrix.lst
|
|
@ -0,0 +1,511 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <forward_list>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <range_algorithm_support.hpp>
|
||||
using namespace std;
|
||||
|
||||
#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s'
|
||||
|
||||
// Test a silly precomposed range adaptor pipeline
|
||||
constexpr auto pipeline = views::drop(1) | views::drop(1) | views::drop(1) | views::drop(1);
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_empty_view = false;
|
||||
template <class T>
|
||||
inline constexpr bool is_empty_view<ranges::empty_view<T>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_dynamic_span = false;
|
||||
template <class T>
|
||||
inline constexpr bool is_dynamic_span<span<T>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_string_view = false;
|
||||
template <class CharT, class Traits>
|
||||
inline constexpr bool is_string_view<basic_string_view<CharT, Traits>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_subrange = false;
|
||||
template <class I, class S, ranges::subrange_kind K>
|
||||
inline constexpr bool is_subrange<ranges::subrange<I, S, K>> = true;
|
||||
|
||||
// clang-format off
|
||||
template <class V>
|
||||
concept reconstructible = ranges::random_access_range<V>
|
||||
&& ranges::sized_range<V>
|
||||
&& (is_empty_view<V>
|
||||
|| is_dynamic_span<V>
|
||||
// || is_string_view<V> // TRANSITION, P1391R4
|
||||
// || is_iota_view<V> // TRANSITION, iota_view
|
||||
|| is_subrange<V>);
|
||||
// clang-format on
|
||||
|
||||
template <ranges::view V>
|
||||
using mapped_t = conditional_t<reconstructible<V>, V, ranges::drop_view<V>>;
|
||||
|
||||
template <ranges::viewable_range Rng>
|
||||
using pipeline_t = mapped_t<mapped_t<mapped_t<mapped_t<views::all_t<Rng>>>>>;
|
||||
|
||||
template <class Rng>
|
||||
concept CanViewDrop = requires(Rng&& r) {
|
||||
views::drop(static_cast<Rng&&>(r), 42);
|
||||
};
|
||||
|
||||
template <ranges::input_range Rng, ranges::random_access_range Expected>
|
||||
constexpr bool test_one(Rng&& rng, Expected&& expected) {
|
||||
using ranges::drop_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev,
|
||||
ranges::range, ranges::sentinel_t, ranges::sized_range;
|
||||
using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range,
|
||||
ranges::contiguous_range;
|
||||
|
||||
constexpr bool is_view = ranges::view<remove_cvref_t<Rng>>;
|
||||
|
||||
using V = views::all_t<Rng>;
|
||||
using M = mapped_t<V>;
|
||||
STATIC_ASSERT(ranges::view<M>);
|
||||
STATIC_ASSERT(common_range<M> == common_range<Rng>);
|
||||
STATIC_ASSERT(input_range<M> == input_range<Rng>);
|
||||
STATIC_ASSERT(forward_range<M> == forward_range<Rng>);
|
||||
STATIC_ASSERT(bidirectional_range<M> == bidirectional_range<Rng>);
|
||||
STATIC_ASSERT(random_access_range<M> == random_access_range<Rng>);
|
||||
STATIC_ASSERT(contiguous_range<M> == contiguous_range<Rng>);
|
||||
|
||||
// Validate range adaptor object and range adaptor closure
|
||||
constexpr auto drop_four = views::drop(4);
|
||||
|
||||
// ... with lvalue argument
|
||||
STATIC_ASSERT(CanViewDrop<Rng&> == (!is_view || copyable<V>) );
|
||||
if constexpr (CanViewDrop<Rng&>) { // Validate lvalue
|
||||
constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v<V> && !is_subrange<V>);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(rng, 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::drop(rng, 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(rng | drop_four), M>);
|
||||
STATIC_ASSERT(noexcept(rng | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(rng | pipeline), pipeline_t<Rng&>>);
|
||||
STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with const lvalue argument
|
||||
STATIC_ASSERT(CanViewDrop<const remove_reference_t<Rng>&> == (!is_view || copyable<V>) );
|
||||
if constexpr (is_view && copyable<V>) {
|
||||
constexpr bool is_noexcept = (is_nothrow_copy_constructible_v<V> && !is_subrange<V>);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(as_const(rng), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | drop_four), M>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | pipeline), pipeline_t<const remove_reference_t<Rng>&>>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept);
|
||||
} else if constexpr (!is_view) {
|
||||
using RC = mapped_t<views::all_t<const remove_reference_t<Rng>&>>;
|
||||
constexpr bool is_noexcept = is_nothrow_constructible_v<RC, const remove_reference_t<Rng>&, int>;
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(as_const(rng), 4)), RC>);
|
||||
STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | drop_four), RC>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | pipeline), pipeline_t<const remove_reference_t<Rng>&>>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with rvalue argument
|
||||
STATIC_ASSERT(CanViewDrop<remove_reference_t<Rng>> == is_view || enable_borrowed_range<remove_cvref_t<Rng>>);
|
||||
if constexpr (is_view) {
|
||||
constexpr bool is_noexcept = is_nothrow_move_constructible_v<V> && !is_subrange<V>;
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(move(rng), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | drop_four), M>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | pipeline), pipeline_t<remove_reference_t<Rng>>>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept);
|
||||
} else if constexpr (enable_borrowed_range<remove_cvref_t<Rng>>) {
|
||||
using S = decltype(ranges::subrange{declval<remove_reference_t<Rng>>()});
|
||||
using RS = drop_view<S>;
|
||||
constexpr bool is_noexcept = noexcept(S{declval<remove_reference_t<Rng>>()});
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(move(rng), 4)), RS>);
|
||||
STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | drop_four), RS>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | pipeline), mapped_t<mapped_t<mapped_t<RS>>>>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with const rvalue argument
|
||||
STATIC_ASSERT(CanViewDrop<const remove_reference_t<Rng>> == (is_view && copyable<V>)
|
||||
|| (!is_view && enable_borrowed_range<remove_cvref_t<Rng>>) );
|
||||
if constexpr (is_view && copyable<V>) {
|
||||
constexpr bool is_noexcept = is_nothrow_copy_constructible_v<V> && !is_subrange<V>;
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(move(as_const(rng)), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | drop_four), M>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | pipeline), pipeline_t<const remove_reference_t<Rng>>>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
|
||||
} else if constexpr (!is_view && enable_borrowed_range<remove_cvref_t<Rng>>) {
|
||||
using S = decltype(ranges::subrange{declval<const remove_reference_t<Rng>>()});
|
||||
using RS = drop_view<S>;
|
||||
constexpr bool is_noexcept = noexcept(S{declval<const remove_reference_t<Rng>>()});
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(move(as_const(rng)), 4)), RS>);
|
||||
STATIC_ASSERT(noexcept(views::drop(move(as_const(rng)), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | drop_four), RS>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | pipeline), mapped_t<mapped_t<mapped_t<RS>>>>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
const bool is_empty = ranges::empty(expected);
|
||||
|
||||
// Validate deduction guide
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442
|
||||
(void) 42;
|
||||
#endif // TRANSITION, DevCom-1159442
|
||||
same_as<drop_view<V>> auto r = drop_view{forward<Rng>(rng), 4};
|
||||
using R = decltype(r);
|
||||
STATIC_ASSERT(ranges::view<R>);
|
||||
STATIC_ASSERT(input_range<R> == input_range<Rng>);
|
||||
STATIC_ASSERT(forward_range<R> == forward_range<Rng>);
|
||||
STATIC_ASSERT(bidirectional_range<R> == bidirectional_range<Rng>);
|
||||
STATIC_ASSERT(random_access_range<R> == random_access_range<Rng>);
|
||||
STATIC_ASSERT(contiguous_range<R> == contiguous_range<Rng>);
|
||||
|
||||
// Validate drop_view::size
|
||||
STATIC_ASSERT(CanMemberSize<R> == CanSize<Rng>);
|
||||
if constexpr (CanMemberSize<R>) {
|
||||
assert(r.size() == static_cast<decltype(r.size())>(ranges::size(expected)));
|
||||
} else {
|
||||
STATIC_ASSERT(!CanSize<R>);
|
||||
}
|
||||
|
||||
// Validate view_interface::empty and operator bool
|
||||
STATIC_ASSERT(CanMemberEmpty<R> == forward_range<Rng>);
|
||||
STATIC_ASSERT(CanBool<R> == CanEmpty<R>);
|
||||
if constexpr (CanMemberEmpty<R>) {
|
||||
assert(r.empty() == is_empty);
|
||||
assert(static_cast<bool>(r) == !is_empty);
|
||||
} else {
|
||||
STATIC_ASSERT(CanEmpty<R> == CanSize<R>);
|
||||
if constexpr (CanEmpty<R>) {
|
||||
assert(ranges::empty(r) == is_empty);
|
||||
assert(static_cast<bool>(r) == !is_empty);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberEmpty<const R> == (random_access_range<const Rng> && sized_range<const Rng>) );
|
||||
STATIC_ASSERT(CanBool<const R> == CanEmpty<const R>);
|
||||
if constexpr (CanMemberEmpty<const R>) {
|
||||
assert(as_const(r).empty() == is_empty);
|
||||
assert(static_cast<bool>(as_const(r)) == !is_empty);
|
||||
} else {
|
||||
STATIC_ASSERT(CanEmpty<const R> == CanSize<const R>);
|
||||
if constexpr (CanEmpty<const R>) {
|
||||
assert(ranges::empty(as_const(r)) == is_empty);
|
||||
assert(static_cast<bool>(as_const(r)) == !is_empty);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate content
|
||||
assert(ranges::equal(r, expected));
|
||||
|
||||
// Validate drop_view::begin
|
||||
STATIC_ASSERT(CanMemberBegin<R>);
|
||||
STATIC_ASSERT(same_as<iterator_t<R>, iterator_t<V>>);
|
||||
STATIC_ASSERT(CanBegin<const R&> == (random_access_range<const V> && sized_range<const V>) );
|
||||
if (forward_range<V>) { // intentionally not if constexpr
|
||||
const same_as<iterator_t<R>> auto i = r.begin();
|
||||
if (!is_empty) {
|
||||
assert(*i == *begin(expected));
|
||||
}
|
||||
|
||||
if constexpr (copyable<V>) {
|
||||
auto r2 = r;
|
||||
const same_as<iterator_t<R>> auto i2 = r2.begin();
|
||||
if (!is_empty) {
|
||||
assert(*i2 == *i);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (random_access_range<const V> && sized_range<const V>) {
|
||||
STATIC_ASSERT(same_as<iterator_t<const R>, iterator_t<const V>>);
|
||||
const same_as<iterator_t<const R>> auto i3 = as_const(r).begin();
|
||||
if (!is_empty) {
|
||||
assert(*i3 == *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate drop_view::end
|
||||
STATIC_ASSERT(CanMemberEnd<R>);
|
||||
STATIC_ASSERT(same_as<sentinel_t<R>, sentinel_t<V>>);
|
||||
STATIC_ASSERT(CanEnd<const R&> == (random_access_range<const V> && sized_range<const V>) );
|
||||
if (!is_empty) {
|
||||
same_as<sentinel_t<R>> auto i = r.end();
|
||||
if constexpr (bidirectional_range<R> && common_range<R>) {
|
||||
assert(*prev(i) == *prev(end(expected)));
|
||||
}
|
||||
|
||||
if constexpr (random_access_range<const V> && sized_range<const V>) {
|
||||
same_as<sentinel_t<const R>> auto i2 = as_const(r).end();
|
||||
if constexpr (bidirectional_range<const R> && common_range<const R>) {
|
||||
assert(*prev(i2) == *prev(end(expected)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::data
|
||||
STATIC_ASSERT(CanMemberData<R> == contiguous_range<V>);
|
||||
STATIC_ASSERT(CanData<R&> == contiguous_range<V>);
|
||||
STATIC_ASSERT(CanData<const R&> == (contiguous_range<const V> && sized_range<const V>) );
|
||||
if constexpr (contiguous_range<V>) {
|
||||
const same_as<remove_reference_t<ranges::range_reference_t<V>>*> auto ptr1 = r.data();
|
||||
assert(to_address(ptr1) == to_address(r.begin()));
|
||||
|
||||
if constexpr (CanData<const R&>) {
|
||||
const same_as<remove_reference_t<ranges::range_reference_t<const V>>*> auto ptr2 = as_const(r).data();
|
||||
assert(to_address(ptr2) == to_address(as_const(r).begin()));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::front and back
|
||||
if (!is_empty) {
|
||||
STATIC_ASSERT(CanMemberFront<R> == forward_range<V>);
|
||||
if constexpr (forward_range<V>) {
|
||||
assert(r.front() == *begin(expected));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberFront<const R> == (random_access_range<const V> && sized_range<const V>) );
|
||||
if constexpr (CanMemberFront<const R>) {
|
||||
assert(as_const(r).front() == *begin(expected));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberBack<R> == (bidirectional_range<R> && common_range<R>) );
|
||||
if constexpr (CanMemberBack<R>) {
|
||||
assert(r.back() == *prev(end(expected)));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(
|
||||
CanMemberBack<const R> == (common_range<const V> && random_access_range<const V> && sized_range<const V>) );
|
||||
if constexpr (CanMemberBack<const R>) {
|
||||
assert(as_const(r).back() == *prev(end(expected)));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::operator[]
|
||||
STATIC_ASSERT(CanIndex<R> == random_access_range<V>);
|
||||
STATIC_ASSERT(CanIndex<const R> == (random_access_range<const V> && sized_range<const V>) );
|
||||
if (!is_empty) {
|
||||
if constexpr (CanIndex<R>) {
|
||||
assert(r[0] == *r.begin());
|
||||
}
|
||||
if constexpr (CanIndex<const R>) {
|
||||
assert(as_const(r)[0] == *as_const(r).begin());
|
||||
}
|
||||
}
|
||||
|
||||
// Validate drop_view::base() const&
|
||||
STATIC_ASSERT(CanMemberBase<const R&> == copy_constructible<V>);
|
||||
if constexpr (CanMemberBase<const R&> && forward_range<V>) {
|
||||
same_as<V> auto b1 = as_const(r).base();
|
||||
STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v<V>);
|
||||
if (!is_empty) {
|
||||
assert(*b1.begin() == 0); // NB: depends on the test data
|
||||
if constexpr (bidirectional_range<V> && common_range<V>) {
|
||||
assert(*prev(b1.end()) == 7); // NB: depends on the test data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate drop_view::base() && (NB: do this last since it leaves r moved-from)
|
||||
if (forward_range<V>) { // intentionally not if constexpr
|
||||
same_as<V> auto b2 = move(r).base();
|
||||
STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v<V>);
|
||||
if (!is_empty) {
|
||||
assert(*b2.begin() == 0); // NB: depends on the test data
|
||||
if constexpr (bidirectional_range<V> && common_range<V>) {
|
||||
assert(*prev(b2.end()) == 7); // NB: depends on the test data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
static constexpr int only_four_ints[] = {4, 5, 6, 7};
|
||||
|
||||
struct instantiator {
|
||||
template <ranges::input_range R>
|
||||
static constexpr void call() {
|
||||
R r{some_ints};
|
||||
test_one(r, only_four_ints);
|
||||
|
||||
R empty_range{};
|
||||
test_one(empty_range, span<const int, 0>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <class Category, test::Sized IsSized, test::Common IsCommon>
|
||||
using test_range =
|
||||
test::range<Category, const int, IsSized, test::CanDifference{derived_from<Category, random_access_iterator_tag>},
|
||||
IsCommon, test::CanCompare{derived_from<Category, forward_iterator_tag> || IsCommon == test::Common::yes},
|
||||
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}>;
|
||||
|
||||
constexpr void instantiation_test() {
|
||||
#ifdef TEST_EVERYTHING
|
||||
test_in<instantiator, const int>();
|
||||
#else // ^^^ test all input range permutations / test only "interesting" permutations vvv
|
||||
// The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness.
|
||||
using test::Common, test::Sized;
|
||||
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::yes, Common::yes>>();
|
||||
#endif // TEST_EVERYTHING
|
||||
}
|
||||
|
||||
template <class Category, test::Sized IsSized, test::Common IsCommon>
|
||||
using move_only_view = test::range<Category, const int, IsSized,
|
||||
test::CanDifference{derived_from<Category, random_access_iterator_tag>}, IsCommon,
|
||||
test::CanCompare{derived_from<Category, forward_iterator_tag> || IsCommon == test::Common::yes},
|
||||
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}, test::CanView::yes, test::Copyability::move_only>;
|
||||
|
||||
constexpr void move_only_test() {
|
||||
using test::Common, test::Sized;
|
||||
using input = input_iterator_tag;
|
||||
using fwd = forward_iterator_tag;
|
||||
using bidi = bidirectional_iterator_tag;
|
||||
using random = random_access_iterator_tag;
|
||||
using contiguous = contiguous_iterator_tag;
|
||||
|
||||
test_one(move_only_view<input, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<input, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
}
|
||||
|
||||
constexpr void output_range_test() {
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163
|
||||
if (!is_constant_evaluated())
|
||||
#endif // TRANSITION, VSO-938163
|
||||
{
|
||||
using R = test::range<output_iterator_tag, int, test::Sized::no, test::CanDifference::no, test::Common::no,
|
||||
test::CanCompare::no, test::ProxyRef::yes, test::CanView::yes, test::Copyability::move_only>;
|
||||
int some_writable_ints[] = {0, 1, 2, 3};
|
||||
STATIC_ASSERT(same_as<decltype(views::drop(R{some_writable_ints}, 2)), ranges::drop_view<R>>);
|
||||
ranges::fill(R{some_writable_ints} | views::drop(2), 42);
|
||||
assert(ranges::equal(some_writable_ints, initializer_list<int>{0, 1, 42, 42}));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Validate views
|
||||
{ // ... copyable
|
||||
// Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view
|
||||
constexpr span<const int> s{some_ints};
|
||||
STATIC_ASSERT(test_one(s, only_four_ints));
|
||||
test_one(s, only_four_ints);
|
||||
|
||||
STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints));
|
||||
test_one(ranges::subrange{some_ints}, only_four_ints);
|
||||
|
||||
STATIC_ASSERT(test_one(views::empty<int>, span<const int, 0>{}));
|
||||
test_one(views::empty<int>, span<const int, 0>{});
|
||||
|
||||
// TRANSITION, P1391R4
|
||||
// STATIC_ASSERT(test_one(basic_string_view{some_ints}, only_four_ints));
|
||||
// test_one(basic_string_view{some_ints}, only_four_ints);
|
||||
|
||||
// TRANSITION, iota_view
|
||||
// STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints));
|
||||
// test_one(ranges::iota_view{0, 8}, only_four_ints);
|
||||
}
|
||||
// ... move-only
|
||||
STATIC_ASSERT((move_only_test(), true));
|
||||
move_only_test();
|
||||
|
||||
// Validate non-views
|
||||
{
|
||||
STATIC_ASSERT(test_one(some_ints, only_four_ints));
|
||||
test_one(some_ints, only_four_ints);
|
||||
}
|
||||
{
|
||||
vector vec(ranges::begin(some_ints), ranges::end(some_ints));
|
||||
test_one(vec, only_four_ints);
|
||||
}
|
||||
{
|
||||
forward_list lst(ranges::begin(some_ints), ranges::end(some_ints));
|
||||
test_one(lst, only_four_ints);
|
||||
}
|
||||
|
||||
// Validate a non-view borrowed range
|
||||
{
|
||||
constexpr span s{some_ints};
|
||||
STATIC_ASSERT(test_one(s, only_four_ints));
|
||||
test_one(s, only_four_ints);
|
||||
}
|
||||
|
||||
// Validate an output range
|
||||
STATIC_ASSERT((output_range_test(), true));
|
||||
output_range_test();
|
||||
|
||||
STATIC_ASSERT((instantiation_test(), true));
|
||||
instantiation_test();
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\strict_concepts_matrix.lst
|
|
@ -0,0 +1,534 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <forward_list>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <range_algorithm_support.hpp>
|
||||
using namespace std;
|
||||
|
||||
#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s'
|
||||
|
||||
// Test a silly precomposed range adaptor pipeline
|
||||
constexpr auto pipeline = views::take(7) | views::take(6) | views::take(5) | views::take(4);
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_empty_view = false;
|
||||
template <class T>
|
||||
inline constexpr bool is_empty_view<ranges::empty_view<T>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_dynamic_span = false;
|
||||
template <class T>
|
||||
inline constexpr bool is_dynamic_span<span<T>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_string_view = false;
|
||||
template <class CharT, class Traits>
|
||||
inline constexpr bool is_string_view<basic_string_view<CharT, Traits>> = true;
|
||||
|
||||
template <class>
|
||||
inline constexpr bool is_subrange = false;
|
||||
template <class I, class S, ranges::subrange_kind K>
|
||||
inline constexpr bool is_subrange<ranges::subrange<I, S, K>> = true;
|
||||
|
||||
// clang-format off
|
||||
template <class V>
|
||||
concept reconstructible = ranges::random_access_range<V>
|
||||
&& ranges::sized_range<V>
|
||||
&& (is_empty_view<V>
|
||||
|| is_dynamic_span<V>
|
||||
// || is_string_view<V> // TRANSITION, P1391R4
|
||||
// || is_iota_view<V> // TRANSITION, iota_view
|
||||
|| is_subrange<V>);
|
||||
// clang-format on
|
||||
|
||||
template <ranges::view V>
|
||||
using mapped_t = conditional_t<reconstructible<V>, V, ranges::take_view<V>>;
|
||||
|
||||
template <ranges::viewable_range Rng>
|
||||
using pipeline_t = mapped_t<mapped_t<mapped_t<mapped_t<views::all_t<Rng>>>>>;
|
||||
|
||||
template <class Rng>
|
||||
concept CanViewTake = requires(Rng&& r) {
|
||||
views::take(static_cast<Rng&&>(r), 42);
|
||||
};
|
||||
|
||||
template <ranges::input_range Rng, ranges::random_access_range Expected>
|
||||
constexpr bool test_one(Rng&& rng, Expected&& expected) {
|
||||
using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range,
|
||||
ranges::contiguous_range;
|
||||
using ranges::take_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev,
|
||||
ranges::range, ranges::sentinel_t, ranges::sized_range;
|
||||
|
||||
constexpr bool is_view = ranges::view<remove_cvref_t<Rng>>;
|
||||
|
||||
using V = views::all_t<Rng>;
|
||||
using M = mapped_t<V>;
|
||||
STATIC_ASSERT(ranges::view<M>);
|
||||
STATIC_ASSERT(input_range<M> == input_range<Rng>);
|
||||
STATIC_ASSERT(forward_range<M> == forward_range<Rng>);
|
||||
STATIC_ASSERT(bidirectional_range<M> == bidirectional_range<Rng>);
|
||||
STATIC_ASSERT(random_access_range<M> == random_access_range<Rng>);
|
||||
STATIC_ASSERT(contiguous_range<M> == contiguous_range<Rng>);
|
||||
|
||||
// Validate range adaptor object and range adaptor closure
|
||||
constexpr auto take_four = views::take(4);
|
||||
|
||||
// ... with lvalue argument
|
||||
STATIC_ASSERT(CanViewTake<Rng&> == (!is_view || copyable<V>) );
|
||||
if constexpr (CanViewTake<Rng&>) { // Validate lvalue
|
||||
constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v<V> && !is_subrange<V>);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(rng, 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::take(rng, 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(rng | take_four), M>);
|
||||
STATIC_ASSERT(noexcept(rng | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(rng | pipeline), pipeline_t<Rng&>>);
|
||||
STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with const lvalue argument
|
||||
STATIC_ASSERT(CanViewTake<const remove_reference_t<Rng>&> == (!is_view || copyable<V>) );
|
||||
if constexpr (is_view && copyable<V>) {
|
||||
constexpr bool is_noexcept = (is_nothrow_copy_constructible_v<V> && !is_subrange<V>);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(as_const(rng), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | take_four), M>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | pipeline), pipeline_t<const remove_reference_t<Rng>&>>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept);
|
||||
} else if constexpr (!is_view) {
|
||||
using RC = mapped_t<views::all_t<const remove_reference_t<Rng>&>>;
|
||||
constexpr bool is_noexcept = is_nothrow_constructible_v<RC, const remove_reference_t<Rng>&, int>;
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(as_const(rng), 4)), RC>);
|
||||
STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | take_four), RC>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(as_const(rng) | pipeline), pipeline_t<const remove_reference_t<Rng>&>>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with rvalue argument
|
||||
STATIC_ASSERT(CanViewTake<remove_reference_t<Rng>> == is_view || enable_borrowed_range<remove_cvref_t<Rng>>);
|
||||
if constexpr (is_view) {
|
||||
constexpr bool is_noexcept = is_nothrow_move_constructible_v<V> && !is_subrange<V>;
|
||||
STATIC_ASSERT(same_as<decltype(views::take(move(rng), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | take_four), M>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | pipeline), pipeline_t<remove_reference_t<Rng>>>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept);
|
||||
} else if constexpr (enable_borrowed_range<remove_cvref_t<Rng>>) {
|
||||
using S = decltype(ranges::subrange{declval<remove_reference_t<Rng>>()});
|
||||
using RS = take_view<S>;
|
||||
constexpr bool is_noexcept = noexcept(S{declval<remove_reference_t<Rng>>()});
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(move(rng), 4)), RS>);
|
||||
STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | take_four), RS>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(rng) | pipeline), mapped_t<mapped_t<mapped_t<RS>>>>);
|
||||
STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
// ... with const rvalue argument
|
||||
STATIC_ASSERT(CanViewTake<const remove_reference_t<Rng>> == (is_view && copyable<V>)
|
||||
|| (!is_view && enable_borrowed_range<remove_cvref_t<Rng>>) );
|
||||
if constexpr (is_view && copyable<V>) {
|
||||
constexpr bool is_noexcept = is_nothrow_copy_constructible_v<V> && !is_subrange<V>;
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(move(as_const(rng)), 4)), M>);
|
||||
STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | take_four), M>);
|
||||
STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | pipeline), pipeline_t<const remove_reference_t<Rng>>>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
|
||||
} else if constexpr (!is_view && enable_borrowed_range<remove_cvref_t<Rng>>) {
|
||||
using S = decltype(ranges::subrange{declval<const remove_reference_t<Rng>>()});
|
||||
using RS = take_view<S>;
|
||||
constexpr bool is_noexcept = noexcept(S{declval<const remove_reference_t<Rng>>()});
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(views::take(move(as_const(rng)), 4)), RS>);
|
||||
STATIC_ASSERT(noexcept(views::take(move(as_const(rng)), 4)) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | take_four), RS>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | take_four) == is_noexcept);
|
||||
|
||||
STATIC_ASSERT(same_as<decltype(move(as_const(rng)) | pipeline), mapped_t<mapped_t<mapped_t<RS>>>>);
|
||||
STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
|
||||
}
|
||||
|
||||
const bool is_empty = ranges::empty(expected);
|
||||
|
||||
// Validate deduction guide
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442
|
||||
(void) 42;
|
||||
#endif // TRANSITION, DevCom-1159442
|
||||
same_as<take_view<V>> auto r = take_view{forward<Rng>(rng), 4};
|
||||
using R = decltype(r);
|
||||
STATIC_ASSERT(ranges::view<R>);
|
||||
STATIC_ASSERT(input_range<R> == input_range<Rng>);
|
||||
STATIC_ASSERT(forward_range<R> == forward_range<Rng>);
|
||||
STATIC_ASSERT(bidirectional_range<R> == bidirectional_range<Rng>);
|
||||
STATIC_ASSERT(random_access_range<R> == random_access_range<Rng>);
|
||||
STATIC_ASSERT(contiguous_range<R> == contiguous_range<Rng>);
|
||||
|
||||
// Validate take_view::size
|
||||
STATIC_ASSERT(CanMemberSize<R> == CanSize<Rng>);
|
||||
if constexpr (CanMemberSize<R>) {
|
||||
assert(r.size() == static_cast<decltype(r.size())>(ranges::size(expected)));
|
||||
} else {
|
||||
STATIC_ASSERT(!CanSize<R>);
|
||||
}
|
||||
|
||||
// Validate view_interface::empty and operator bool
|
||||
STATIC_ASSERT(CanMemberEmpty<R> == forward_range<Rng>);
|
||||
STATIC_ASSERT(CanBool<R> == CanEmpty<R>);
|
||||
if constexpr (CanMemberEmpty<R>) {
|
||||
assert(r.empty() == is_empty);
|
||||
assert(static_cast<bool>(r) == !is_empty);
|
||||
} else {
|
||||
STATIC_ASSERT(CanEmpty<R> == CanSize<R>);
|
||||
if constexpr (CanEmpty<R>) {
|
||||
assert(ranges::empty(r) == is_empty);
|
||||
assert(static_cast<bool>(r) == !is_empty);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberEmpty<const R> == forward_range<const Rng>);
|
||||
STATIC_ASSERT(CanBool<const R> == CanEmpty<const R>);
|
||||
if constexpr (CanMemberEmpty<const R>) {
|
||||
assert(as_const(r).empty() == is_empty);
|
||||
assert(static_cast<bool>(as_const(r)) == !is_empty);
|
||||
} else {
|
||||
STATIC_ASSERT(CanEmpty<const R> == CanSize<const R>);
|
||||
if constexpr (CanEmpty<const R>) {
|
||||
assert(ranges::empty(as_const(r)) == is_empty);
|
||||
assert(static_cast<bool>(as_const(r)) == !is_empty);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate content
|
||||
assert(ranges::equal(r, expected));
|
||||
|
||||
// Validate take_view::begin
|
||||
STATIC_ASSERT(CanMemberBegin<R>);
|
||||
if constexpr (random_access_range<V> && sized_range<V>) {
|
||||
STATIC_ASSERT(same_as<iterator_t<R>, iterator_t<V>>);
|
||||
} else {
|
||||
STATIC_ASSERT(same_as<iterator_t<R>, counted_iterator<iterator_t<V>>>);
|
||||
}
|
||||
STATIC_ASSERT(CanBegin<const R&> == range<const V>);
|
||||
if (forward_range<V>) { // intentionally not if constexpr
|
||||
const same_as<iterator_t<R>> auto i = r.begin();
|
||||
if (!is_empty) {
|
||||
assert(*i == *begin(expected));
|
||||
}
|
||||
|
||||
if constexpr (copyable<V>) {
|
||||
auto r2 = r;
|
||||
const same_as<iterator_t<R>> auto i2 = r2.begin();
|
||||
if (!is_empty) {
|
||||
assert(*i2 == *i);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (range<const V>) {
|
||||
if constexpr (random_access_range<const V> && sized_range<const V>) {
|
||||
STATIC_ASSERT(same_as<iterator_t<const R>, iterator_t<const V>>);
|
||||
} else {
|
||||
STATIC_ASSERT(same_as<iterator_t<const R>, counted_iterator<iterator_t<const V>>>);
|
||||
}
|
||||
|
||||
const same_as<iterator_t<const R>> auto i3 = as_const(r).begin();
|
||||
if (!is_empty) {
|
||||
assert(*i3 == *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate take_view::end
|
||||
STATIC_ASSERT(CanMemberEnd<R>);
|
||||
if constexpr (sized_range<V>) {
|
||||
if constexpr (random_access_range<V>) {
|
||||
STATIC_ASSERT(same_as<sentinel_t<R>, iterator_t<V>>);
|
||||
} else {
|
||||
STATIC_ASSERT(same_as<sentinel_t<R>, default_sentinel_t>);
|
||||
}
|
||||
} else {
|
||||
// Not much we can do here
|
||||
STATIC_ASSERT(!same_as<sentinel_t<R>, iterator_t<V>>);
|
||||
STATIC_ASSERT(!same_as<sentinel_t<R>, default_sentinel_t>);
|
||||
STATIC_ASSERT(is_class_v<sentinel_t<R>>);
|
||||
}
|
||||
STATIC_ASSERT(CanEnd<const R&> == range<const V>);
|
||||
if (!is_empty) {
|
||||
same_as<sentinel_t<R>> auto i = r.end();
|
||||
if constexpr (bidirectional_range<R> && common_range<R>) {
|
||||
assert(*prev(i) == *prev(end(expected)));
|
||||
}
|
||||
|
||||
if constexpr (range<const V>) {
|
||||
same_as<sentinel_t<const R>> auto i2 = as_const(r).end();
|
||||
if constexpr (bidirectional_range<const R> && common_range<const R>) {
|
||||
assert(*prev(i2) == *prev(end(expected)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::data
|
||||
STATIC_ASSERT(CanMemberData<R> == contiguous_range<V>);
|
||||
STATIC_ASSERT(CanData<R&> == contiguous_range<V>);
|
||||
STATIC_ASSERT(CanData<const R&> == contiguous_range<const V>);
|
||||
if constexpr (contiguous_range<V>) {
|
||||
const same_as<remove_reference_t<ranges::range_reference_t<V>>*> auto ptr1 = r.data();
|
||||
assert(to_address(ptr1) == to_address(r.begin()));
|
||||
|
||||
if constexpr (contiguous_range<const V>) {
|
||||
const same_as<remove_reference_t<ranges::range_reference_t<const V>>*> auto ptr2 = as_const(r).data();
|
||||
assert(to_address(ptr2) == to_address(as_const(r).begin()));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::front and back
|
||||
if (!is_empty) {
|
||||
STATIC_ASSERT(CanMemberFront<R> == forward_range<V>);
|
||||
if constexpr (forward_range<V>) {
|
||||
assert(r.front() == *begin(expected));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberFront<const R> == forward_range<const V>);
|
||||
if constexpr (forward_range<const V>) {
|
||||
assert(as_const(r).front() == *begin(expected));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberBack<R> == (bidirectional_range<R> && common_range<R>) );
|
||||
if constexpr (CanMemberBack<R>) {
|
||||
assert(r.back() == *prev(end(expected)));
|
||||
}
|
||||
|
||||
STATIC_ASSERT(CanMemberBack<const R> == (bidirectional_range<const R> && common_range<const R>) );
|
||||
if constexpr (CanMemberBack<const R>) {
|
||||
assert(as_const(r).back() == *prev(end(expected)));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate view_interface::operator[]
|
||||
STATIC_ASSERT(CanIndex<R> == random_access_range<V>);
|
||||
STATIC_ASSERT(CanIndex<const R> == random_access_range<const V>);
|
||||
if (!is_empty) {
|
||||
if constexpr (CanIndex<R>) {
|
||||
assert(r[0] == *r.begin());
|
||||
}
|
||||
if constexpr (CanIndex<const R>) {
|
||||
assert(as_const(r)[0] == *as_const(r).begin());
|
||||
}
|
||||
}
|
||||
|
||||
// Validate take_view::base() const&
|
||||
STATIC_ASSERT(CanMemberBase<const R&> == copy_constructible<V>);
|
||||
if constexpr (copy_constructible<V> && forward_range<V>) {
|
||||
same_as<V> auto b1 = as_const(r).base();
|
||||
STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v<V>);
|
||||
if (!is_empty) {
|
||||
assert(*b1.begin() == *begin(expected));
|
||||
if constexpr (bidirectional_range<V> && common_range<V>) {
|
||||
assert(*prev(b1.end()) == 7); // NB: depends on the test data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate take_view::base() && (NB: do this last since it leaves r moved-from)
|
||||
if (forward_range<V>) { // intentionally not if constexpr
|
||||
same_as<V> auto b2 = move(r).base();
|
||||
STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v<V>);
|
||||
if (!is_empty) {
|
||||
assert(*b2.begin() == *begin(expected));
|
||||
if constexpr (bidirectional_range<V> && common_range<V>) {
|
||||
assert(*prev(b2.end()) == 7); // NB: depends on the test data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
static constexpr int only_four_ints[] = {0, 1, 2, 3};
|
||||
|
||||
struct instantiator {
|
||||
template <ranges::input_range R>
|
||||
static constexpr void call() {
|
||||
R r{some_ints};
|
||||
test_one(r, only_four_ints);
|
||||
|
||||
R empty_range{};
|
||||
test_one(empty_range, span<const int, 0>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <class Category, test::Sized IsSized, test::Common IsCommon>
|
||||
using test_range =
|
||||
test::range<Category, const int, IsSized, test::CanDifference{derived_from<Category, random_access_iterator_tag>},
|
||||
IsCommon, test::CanCompare{derived_from<Category, forward_iterator_tag> || IsCommon == test::Common::yes},
|
||||
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}>;
|
||||
|
||||
constexpr void instantiation_test() {
|
||||
#ifdef TEST_EVERYTHING
|
||||
test_in<instantiator, const int>();
|
||||
#else // ^^^ test all input range permutations / test only "interesting" permutations vvv
|
||||
// The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness.
|
||||
using test::Common, test::Sized;
|
||||
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<input_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<forward_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<bidirectional_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<random_access_iterator_tag, Sized::yes, Common::yes>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::no, Common::no>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::yes, Common::no>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::no, Common::yes>>();
|
||||
instantiator::call<test_range<contiguous_iterator_tag, Sized::yes, Common::yes>>();
|
||||
#endif // TEST_EVERYTHING
|
||||
}
|
||||
|
||||
template <class Category, test::Sized IsSized, test::Common IsCommon>
|
||||
using move_only_view = test::range<Category, const int, IsSized,
|
||||
test::CanDifference{derived_from<Category, random_access_iterator_tag>}, IsCommon,
|
||||
test::CanCompare{derived_from<Category, forward_iterator_tag> || IsCommon == test::Common::yes},
|
||||
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}, test::CanView::yes, test::Copyability::move_only>;
|
||||
|
||||
constexpr void move_only_test() {
|
||||
using test::Common, test::Sized;
|
||||
using input = input_iterator_tag;
|
||||
using fwd = forward_iterator_tag;
|
||||
using bidi = bidirectional_iterator_tag;
|
||||
using random = random_access_iterator_tag;
|
||||
using contiguous = contiguous_iterator_tag;
|
||||
|
||||
test_one(move_only_view<input, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<input, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<fwd, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<bidi, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<random, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::no, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::yes, Common::no>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::no, Common::yes>{some_ints}, only_four_ints);
|
||||
test_one(move_only_view<contiguous, Sized::yes, Common::yes>{some_ints}, only_four_ints);
|
||||
}
|
||||
|
||||
constexpr void output_range_test() {
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163
|
||||
if (!is_constant_evaluated())
|
||||
#endif // TRANSITION, VSO-938163
|
||||
{
|
||||
using R = test::range<output_iterator_tag, int, test::Sized::no, test::CanDifference::no, test::Common::no,
|
||||
test::CanCompare::no, test::ProxyRef::yes, test::CanView::yes, test::Copyability::move_only>;
|
||||
int some_writable_ints[] = {0, 1, 2, 3};
|
||||
STATIC_ASSERT(same_as<decltype(views::take(R{some_writable_ints}, 99999)), ranges::take_view<R>>);
|
||||
|
||||
// How do I implement "Fill up to n elements in {output range} with {value}"?
|
||||
ranges::fill(R{some_writable_ints} | views::take(99999), 42);
|
||||
assert(ranges::equal(some_writable_ints, initializer_list<int>{42, 42, 42, 42}));
|
||||
|
||||
ranges::fill(R{some_writable_ints} | views::take(3), 13);
|
||||
assert(ranges::equal(some_writable_ints, initializer_list<int>{13, 13, 13, 42}));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Validate views
|
||||
{ // ... copyable
|
||||
// Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view
|
||||
constexpr span<const int> s{some_ints};
|
||||
STATIC_ASSERT(test_one(s, only_four_ints));
|
||||
test_one(s, only_four_ints);
|
||||
|
||||
STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints));
|
||||
test_one(ranges::subrange{some_ints}, only_four_ints);
|
||||
|
||||
STATIC_ASSERT(test_one(views::empty<int>, span<const int, 0>{}));
|
||||
test_one(views::empty<int>, span<const int, 0>{});
|
||||
|
||||
// TRANSITION, P1391R4
|
||||
// STATIC_ASSERT(test_one(basic_string_view{some_ints}, only_four_ints));
|
||||
// test_one(basic_string_view{some_ints}, only_four_ints);
|
||||
|
||||
// TRANSITION, iota_view
|
||||
// STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints));
|
||||
// test_one(ranges::iota_view{0, 8}, only_four_ints);
|
||||
}
|
||||
// ... move-only
|
||||
STATIC_ASSERT((move_only_test(), true));
|
||||
move_only_test();
|
||||
|
||||
// Validate non-views
|
||||
{
|
||||
STATIC_ASSERT(test_one(some_ints, only_four_ints));
|
||||
test_one(some_ints, only_four_ints);
|
||||
}
|
||||
{
|
||||
vector vec(ranges::begin(some_ints), ranges::end(some_ints));
|
||||
test_one(vec, only_four_ints);
|
||||
}
|
||||
{
|
||||
forward_list lst(ranges::begin(some_ints), ranges::end(some_ints));
|
||||
test_one(lst, only_four_ints);
|
||||
}
|
||||
|
||||
// Validate a non-view borrowed range
|
||||
{
|
||||
constexpr span s{some_ints};
|
||||
STATIC_ASSERT(test_one(s, only_four_ints));
|
||||
test_one(s, only_four_ints);
|
||||
}
|
||||
|
||||
// Validate an output range
|
||||
STATIC_ASSERT((output_range_test(), true));
|
||||
output_range_test();
|
||||
|
||||
STATIC_ASSERT((instantiation_test(), true));
|
||||
instantiation_test();
|
||||
}
|
Загрузка…
Ссылка в новой задаче