Implement views::take and views::drop (#1253)

Also implements the proposed resolution of LWG-3481.
This commit is contained in:
Casey Carter 2020-09-04 17:20:27 -07:00 коммит произвёл GitHub
Родитель 6156ec32d5
Коммит 0c2007a67b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 1549 добавлений и 23 удалений

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

@ -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

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

@ -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();
}