Implement views::all and views::reverse (#1229)

* Implement views:all and views::reverse

Also, perma-workaround LLVM-37556.

Co-authored-by: Michael Schellenberger Costa <mschellenbergercosta@gmail.com>
This commit is contained in:
Casey Carter 2020-08-31 21:07:00 -07:00 коммит произвёл GitHub
Родитель 3b2956b18e
Коммит e652e72bdf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 1707 добавлений и 549 удалений

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

@ -165,9 +165,10 @@ namespace ranges {
}; };
} // namespace _Swap } // namespace _Swap
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Swap::_Cpo swap; inline constexpr _Swap::_Cpo swap;
} }
using namespace _Cpos;
} // namespace ranges } // namespace ranges
// CONCEPT swappable // CONCEPT swappable

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

@ -33,10 +33,192 @@ namespace ranges {
concept viewable_range = range<_Rng> concept viewable_range = range<_Rng>
&& (borrowed_range<_Rng> || view<remove_cvref_t<_Rng>>); && (borrowed_range<_Rng> || view<remove_cvref_t<_Rng>>);
template <class _Rng>
concept _Simple_view = view<_Rng> && range<const _Rng>
&& same_as<iterator_t<_Rng>, iterator_t<const _Rng>>
&& same_as<sentinel_t<_Rng>, sentinel_t<const _Rng>>;
template <class _Ty> template <class _Ty>
concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>; concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>;
// clang-format on // clang-format on
namespace _Pipe {
// clang-format off
template <class _Left, class _Right>
concept _Can_pipe = requires(_Left&& __l, _Right&& __r) {
static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l));
};
template <class _Left, class _Right>
concept _Can_compose = constructible_from<remove_cvref_t<_Left>, _Left>
&& constructible_from<remove_cvref_t<_Right>, _Right>;
// clang-format on
template <class, class>
struct _Pipeline;
template <class _Derived>
struct _Base {
// clang-format off
template <class _Other>
requires _Can_compose<_Derived, _Other>
constexpr auto operator|(_Base<_Other>&& __r) && noexcept(
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) {
// clang-format on
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)};
}
// clang-format off
template <class _Other>
requires _Can_compose<_Derived, const _Other&>
constexpr auto operator|(const _Base<_Other>& __r) && noexcept(noexcept(
_Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)})) {
// clang-format on
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)};
}
// clang-format off
template <class _Other>
requires _Can_compose<const _Derived&, _Other>
constexpr auto operator|(_Base<_Other>&& __r) const& noexcept(
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)})) {
// clang-format on
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)};
}
// clang-format off
template <class _Other>
requires _Can_compose<const _Derived&, const _Other&>
constexpr auto operator|(const _Base<_Other>& __r) const& noexcept(noexcept(
_Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)})) {
// clang-format on
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)};
}
template <_Can_pipe<const _Derived&> _Left>
friend constexpr auto operator|(_Left&& __l, const _Base& __r) noexcept(
noexcept(static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l)))) {
return static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l));
}
template <_Can_pipe<_Derived> _Left>
friend constexpr auto operator|(_Left&& __l, _Base&& __r) noexcept(
noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)))) {
return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l));
}
};
template <class _Left, class _Right>
struct _Pipeline : _Base<_Pipeline<_Left, _Right>> {
/* [[no_unique_address]] */ _Left __l;
/* [[no_unique_address]] */ _Right __r;
template <class _Ty1, class _Ty2>
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>)
: __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {}
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept(
noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }
};
template <class _Ty1, class _Ty2>
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;
} // namespace _Pipe
template <range _Rng, class _Derived>
class _Cached_position : public view_interface<_Derived> {
static_assert(_Always_false<_Rng>, "A range must be at least forward for position caching to be worthwhile.");
};
template <forward_range _Rng, class _Derived>
class _Cached_position<_Rng, _Derived> : public view_interface<_Derived> {
private:
using _It = iterator_t<_Rng>;
/* [[no_unique_address]] */ _It _Pos{};
bool _Cached = false;
protected:
_Cached_position() = default;
~_Cached_position() = default;
// a copied iterator doesn't point into a copied range, so cache values must not propagate via copy
constexpr _Cached_position(const _Cached_position&) noexcept(is_nothrow_default_constructible_v<_It>) {}
constexpr _Cached_position& operator=(const _Cached_position&) noexcept(
is_nothrow_default_constructible_v<_It>) {
_Pos = _It{};
_Cached = false;
return *this;
}
_NODISCARD constexpr bool _Has_cache() const noexcept { // Is there a cached position?
return _Cached;
}
_NODISCARD constexpr _It _Get_cache(_Rng&) const noexcept(is_nothrow_copy_constructible_v<_It>) {
_STL_INTERNAL_CHECK(_Cached);
return _Pos;
}
_NODISCARD constexpr void _Set_cache(_Rng&, _It _Iter) noexcept(is_nothrow_move_assignable_v<_It>) {
_Pos = _STD move(_Iter);
_Cached = true;
}
};
template <random_access_range _Rng, class _Derived>
class _Cached_position<_Rng, _Derived> : public view_interface<_Derived> {
private:
using _It = iterator_t<_Rng>;
range_difference_t<_Rng> _Off = -1;
protected:
_Cached_position() = default;
~_Cached_position() = default;
// Offsets are oblivious to copying, so cache values _do_ propagate via copying.
_Cached_position(const _Cached_position&) = default;
_Cached_position& operator=(const _Cached_position&) = default;
_NODISCARD constexpr bool _Has_cache() const noexcept { // Is there a cached position?
return _Off >= range_difference_t<_Rng>{0};
}
_NODISCARD constexpr _It _Get_cache(_Rng& _Range) const noexcept(noexcept(_RANGES begin(_Range) + _Off)) {
_STL_INTERNAL_CHECK(_Has_cache());
return _RANGES begin(_Range) + _Off;
}
_NODISCARD constexpr void _Set_cache(_Rng& _Range, const _It& _Iter) noexcept(
noexcept(_Off = _Iter - _RANGES begin(_Range))) {
_Off = _Iter - _RANGES begin(_Range);
}
};
template <bool _Enable, class _Rng, class _Derived>
using _Cached_position_t = conditional_t<_Enable, _Cached_position<_Rng, _Derived>, view_interface<_Derived>>;
// CLASS TEMPLATE ranges::_Semiregular_box // CLASS TEMPLATE ranges::_Semiregular_box
#if 0 // TRANSITION, VSO-1174090 #if 0 // TRANSITION, VSO-1174090
template <_Copy_constructible_object _Ty> template <_Copy_constructible_object _Ty>
@ -417,10 +599,10 @@ namespace ranges {
// clang-format off // clang-format off
template <class _Ty> template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept( _NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept(
noexcept(single_view{static_cast<_Ty&&>(_Val)})) requires requires { noexcept(single_view{_STD forward<_Ty>(_Val)})) requires requires {
single_view{static_cast<_Ty&&>(_Val)}; single_view{static_cast<_Ty&&>(_Val)};
} { } {
return single_view{static_cast<_Ty&&>(_Val)}; return single_view{_STD forward<_Ty>(_Val)};
} }
// clang-format on // clang-format on
}; };
@ -486,6 +668,210 @@ namespace ranges {
template <class _Rng> template <class _Rng>
inline constexpr bool enable_borrowed_range<ref_view<_Rng>> = true; inline constexpr bool enable_borrowed_range<ref_view<_Rng>> = true;
namespace views {
// VARIABLE views::all
template <class _Rng>
concept _Can_ref_view = requires(_Rng&& __r) {
ref_view{static_cast<_Rng&&>(__r)};
};
template <class _Rng>
concept _Can_subrange = requires(_Rng&& __r) {
subrange{static_cast<_Rng&&>(__r)};
};
class _All_fn : public _Pipe::_Base<_All_fn> {
private:
enum class _St { _None, _View, _Ref, _Subrange };
template <class _Rng>
_NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept {
if constexpr (view<remove_cvref_t<_Rng>>) {
if constexpr (constructible_from<remove_cvref_t<_Rng>, _Rng>) {
return {_St::_View, is_nothrow_constructible_v<remove_cvref_t<_Rng>, _Rng>};
}
} else if constexpr (_Can_ref_view<_Rng>) {
return {_St::_Ref, noexcept(ref_view{_STD declval<_Rng>()})};
} else if constexpr (_Can_subrange<_Rng>) {
return {_St::_Subrange, noexcept(subrange{_STD declval<_Rng>()})};
}
return {_St::_None};
}
template <class _Rng>
static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>();
public:
// clang-format off
template <viewable_range _Rng>
requires (_Choice<_Rng>._Strategy != _St::_None)
_NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) {
// clang-format on
if constexpr (_Choice<_Rng>._Strategy == _St::_View) {
return _STD forward<_Rng>(_Range);
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Ref) {
return ref_view{_STD forward<_Rng>(_Range)};
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange) {
return subrange{_STD forward<_Rng>(_Range)};
} else {
static_assert(_Always_false<_Rng>, "Should be unreachable");
}
}
};
inline constexpr _All_fn all;
// ALIAS TEMPLATE views::all_t
template <viewable_range _Rng>
using all_t = decltype(all(_STD declval<_Rng>()));
} // namespace views
// CLASS TEMPLATE ranges::reverse_view
// clang-format off
template <view _Vw>
requires bidirectional_range<_Vw>
class reverse_view : public _Cached_position_t<!common_range<_Vw>, _Vw, reverse_view<_Vw>> {
// clang-format on
private:
/* [[no_unique_address]] */ _Vw _Base{};
template <class _Rng>
using _Rev_iter = reverse_iterator<iterator_t<_Rng>>;
public:
reverse_view() = default;
constexpr explicit reverse_view(_Vw _Base_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened
: _Base(_STD move(_Base_)) {}
_NODISCARD constexpr _Vw base() const& noexcept(
is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> {
return _Base;
}
_NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ {
return _STD move(_Base);
}
_NODISCARD constexpr _Rev_iter<_Vw> begin() {
if constexpr (common_range<_Vw>) {
return _Rev_iter<_Vw>{_RANGES end(_Base)};
} else {
if (this->_Has_cache()) {
return _Rev_iter<_Vw>{this->_Get_cache(_Base)};
}
iterator_t<_Vw> _First;
if constexpr (sized_range<_Vw>) {
_First = _RANGES next(_RANGES begin(_Base), _RANGES distance(_Base));
} else {
_First = _RANGES next(_RANGES begin(_Base), _RANGES end(_Base));
}
this->_Set_cache(_Base, _First);
return _Rev_iter<_Vw>{_STD move(_First)};
}
}
_NODISCARD constexpr auto begin() const noexcept(
noexcept(_Rev_iter<const _Vw>{_RANGES end(_Base)})) /* strengthened */ requires common_range<const _Vw> {
return _Rev_iter<const _Vw>{_RANGES end(_Base)};
}
_NODISCARD constexpr _Rev_iter<_Vw> end() noexcept(
noexcept(_Rev_iter<_Vw>{_RANGES begin(_Base)})) /* strengthened */ {
return _Rev_iter<_Vw>{_RANGES begin(_Base)};
}
_NODISCARD constexpr auto end() const noexcept(
noexcept(_Rev_iter<const _Vw>{_RANGES begin(_Base)})) /* strengthened */ requires common_range<const _Vw> {
return _Rev_iter<const _Vw>{_RANGES begin(_Base)};
}
_NODISCARD constexpr auto size() noexcept(
noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range<_Vw> {
return _RANGES size(_Base);
}
_NODISCARD constexpr auto size() const
noexcept(noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range<const _Vw> {
return _RANGES size(_Base);
}
};
template <class _Rng>
reverse_view(_Rng &&) -> reverse_view<views::all_t<_Rng>>;
namespace views {
// VARIABLE views::reverse
template <class _Rng>
concept _Can_extract_base = requires(_Rng&& __r) {
static_cast<_Rng&&>(__r).base();
};
template <class _Rng>
concept _Can_reverse = requires(_Rng&& __r) {
reverse_view{static_cast<_Rng&&>(__r)};
};
class _Reverse_fn : public _Pipe::_Base<_Reverse_fn> {
private:
enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse };
template <class>
static constexpr auto _Reversed_subrange = -1;
template <class _It, subrange_kind _Ki>
static constexpr auto
_Reversed_subrange<subrange<reverse_iterator<_It>, reverse_iterator<_It>, _Ki>> = static_cast<int>(_Ki);
template <class _Rng>
_NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept {
if constexpr (bidirectional_range<_Rng>) {
if constexpr (_Is_specialization_v<_Rng, reverse_view>) {
if constexpr (_Can_extract_base<_Rng>) {
return {_St::_Base, noexcept(_STD declval<_Rng>().base())};
}
} else if constexpr (_Reversed_subrange<remove_cvref_t<_Rng>> == 0) {
using _It = decltype(_STD declval<_Rng&>().begin().base());
return {_St::_Subrange_unsized,
noexcept(subrange<_It, _It, subrange_kind::unsized>{
_STD declval<_Rng&>().end().base(), _STD declval<_Rng&>().begin().base()})};
} else if constexpr (_Reversed_subrange<remove_cvref_t<_Rng>> == 1) {
using _It = decltype(_STD declval<_Rng&>().begin().base());
return {_St::_Subrange_sized,
noexcept(subrange<_It, _It, subrange_kind::sized>{_STD declval<_Rng&>().end().base(),
_STD declval<_Rng&>().begin().base(), _STD declval<_Rng&>().size()})};
} else if constexpr (_Can_reverse<_Rng>) {
return {_St::_Reverse, noexcept(reverse_view{_STD declval<_Rng>()})};
}
}
return {_St::_None};
}
template <class _Rng>
static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>();
public:
// clang-format off
template <viewable_range _Rng>
requires (_Choice<_Rng>._Strategy != _St::_None)
_NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) {
// clang-format on
if constexpr (_Choice<_Rng>._Strategy == _St::_Base) {
return _STD forward<_Rng>(_Range).base();
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange_unsized) {
return subrange{_Range.end().base(), _Range.begin().base()};
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange_sized) {
return subrange{_Range.end().base(), _Range.begin().base(), _Range.size()};
} else if constexpr (_Choice<_Rng>._Strategy == _St::_Reverse) {
return reverse_view{_STD forward<_Rng>(_Range)};
} else {
static_assert(_Always_false<_Rng>, "Should be unreachable");
}
}
};
inline constexpr _Reverse_fn reverse;
} // namespace views
} // namespace ranges } // namespace ranges
namespace views = ranges::views; namespace views = ranges::views;

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

@ -1048,7 +1048,7 @@ public:
constexpr _String_view_iterator& operator+=(const difference_type _Off) noexcept { constexpr _String_view_iterator& operator+=(const difference_type _Off) noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1 #if _ITERATOR_DEBUG_LEVEL >= 1
_Verify_offset(_Off); _Verify_offset(_Off);
_Myoff += _Off; _Myoff += static_cast<size_t>(_Off);
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
_Myptr += _Off; _Myptr += _Off;
#endif // _ITERATOR_DEBUG_LEVEL #endif // _ITERATOR_DEBUG_LEVEL
@ -1077,7 +1077,7 @@ public:
"cannot seek string_view iterator after end"); "cannot seek string_view iterator after end");
} }
_Myoff -= _Off; _Myoff -= static_cast<size_t>(_Off);
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
_Myptr -= _Off; _Myptr -= _Off;
#endif // _ITERATOR_DEBUG_LEVEL #endif // _ITERATOR_DEBUG_LEVEL

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

@ -645,7 +645,7 @@ struct iterator_traits<_Ty*> {
}; };
// clang-format on // clang-format on
// CUSTOMIZATION POINT OBJECT iter_move // CUSTOMIZATION POINT OBJECT ranges::iter_move
namespace ranges { namespace ranges {
// STRUCT TEMPLATE _Choice_t // STRUCT TEMPLATE _Choice_t
template <class _Ty> template <class _Ty>
@ -707,9 +707,10 @@ namespace ranges {
// clang-format on // clang-format on
} // namespace _Iter_move } // namespace _Iter_move
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Iter_move::_Cpo iter_move; inline constexpr _Iter_move::_Cpo iter_move;
} }
using namespace _Cpos;
} // namespace ranges } // namespace ranges
// iter_swap defined below since it depends on indirectly_movable_storable // iter_swap defined below since it depends on indirectly_movable_storable
@ -1017,7 +1018,7 @@ concept indirectly_copyable_storable = indirectly_copyable<_In, _Out>
&& assignable_from<iter_value_t<_In>&, iter_reference_t<_In>>; && assignable_from<iter_value_t<_In>&, iter_reference_t<_In>>;
// clang-format on // clang-format on
// CUSTOMIZATION POINT OBJECT iter_swap // CUSTOMIZATION POINT OBJECT ranges::iter_swap
namespace ranges { namespace ranges {
namespace _Iter_swap { namespace _Iter_swap {
template <class _Ty1, class _Ty2> template <class _Ty1, class _Ty2>
@ -1091,9 +1092,10 @@ namespace ranges {
}; };
} // namespace _Iter_swap } // namespace _Iter_swap
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Iter_swap::_Cpo iter_swap; inline constexpr _Iter_swap::_Cpo iter_swap;
} }
using namespace _Cpos;
} // namespace ranges } // namespace ranges
// clang-format off // clang-format off
@ -2408,9 +2410,10 @@ namespace ranges {
}; };
} // namespace _Begin } // namespace _Begin
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Begin::_Cpo begin; inline constexpr _Begin::_Cpo begin;
} }
using namespace _Cpos;
// ALIAS TEMPLATE ranges::iterator_t // ALIAS TEMPLATE ranges::iterator_t
template <class _Ty> template <class _Ty>
@ -2468,9 +2471,10 @@ namespace ranges {
}; };
} // namespace _Unchecked_begin } // namespace _Unchecked_begin
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Unchecked_begin::_Cpo _Ubegin; inline constexpr _Unchecked_begin::_Cpo _Ubegin;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::end // CUSTOMIZATION POINT OBJECT ranges::end
namespace _End { namespace _End {
@ -2542,9 +2546,10 @@ namespace ranges {
}; };
} // namespace _End } // namespace _End
inline namespace _Cpos { namespace _Cpos {
inline constexpr _End::_Cpo end; inline constexpr _End::_Cpo end;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::_Uend // CUSTOMIZATION POINT OBJECT ranges::_Uend
namespace _Unchecked_end { namespace _Unchecked_end {
@ -2599,9 +2604,10 @@ namespace ranges {
}; };
} // namespace _Unchecked_end } // namespace _Unchecked_end
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Unchecked_end::_Cpo _Uend; inline constexpr _Unchecked_end::_Cpo _Uend;
} }
using namespace _Cpos;
// CONCEPT ranges::range // CONCEPT ranges::range
template <class _Rng> template <class _Rng>
@ -2648,9 +2654,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Cbegin_fn cbegin; inline constexpr _Cbegin_fn cbegin;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::cend // CUSTOMIZATION POINT OBJECT ranges::cend
struct _Cend_fn { struct _Cend_fn {
@ -2664,9 +2671,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Cend_fn cend; inline constexpr _Cend_fn cend;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::rbegin // CUSTOMIZATION POINT OBJECT ranges::rbegin
namespace _Rbegin { namespace _Rbegin {
@ -2733,9 +2741,10 @@ namespace ranges {
}; };
} // namespace _Rbegin } // namespace _Rbegin
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Rbegin::_Cpo rbegin; inline constexpr _Rbegin::_Cpo rbegin;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::rend // CUSTOMIZATION POINT OBJECT ranges::rend
namespace _Rend { namespace _Rend {
@ -2803,9 +2812,10 @@ namespace ranges {
}; };
} // namespace _Rend } // namespace _Rend
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Rend::_Cpo rend; inline constexpr _Rend::_Cpo rend;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::crbegin // CUSTOMIZATION POINT OBJECT ranges::crbegin
struct _Crbegin_fn { struct _Crbegin_fn {
@ -2819,9 +2829,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Crbegin_fn crbegin; inline constexpr _Crbegin_fn crbegin;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::crend // CUSTOMIZATION POINT OBJECT ranges::crend
struct _Crend_fn { struct _Crend_fn {
@ -2835,9 +2846,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Crend_fn crend; inline constexpr _Crend_fn crend;
} }
using namespace _Cpos;
// VARIABLE TEMPLATE ranges::disable_sized_range // VARIABLE TEMPLATE ranges::disable_sized_range
template <class> template <class>
@ -2921,9 +2933,10 @@ namespace ranges {
}; };
} // namespace _Size } // namespace _Size
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Size::_Cpo size; inline constexpr _Size::_Cpo size;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::empty // CUSTOMIZATION POINT OBJECT ranges::empty
namespace _Empty { namespace _Empty {
@ -2989,9 +3002,10 @@ namespace ranges {
}; };
} // namespace _Empty } // namespace _Empty
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Empty::_Cpo empty; inline constexpr _Empty::_Cpo empty;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::data // CUSTOMIZATION POINT OBJECT ranges::data
namespace _Data { namespace _Data {
@ -3046,9 +3060,10 @@ namespace ranges {
}; };
} // namespace _Data } // namespace _Data
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Data::_Cpo data; inline constexpr _Data::_Cpo data;
} }
using namespace _Cpos;
// CUSTOMIZATION POINT OBJECT ranges::cdata // CUSTOMIZATION POINT OBJECT ranges::cdata
struct _Cdata_fn { struct _Cdata_fn {
@ -3062,9 +3077,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Cdata_fn cdata; inline constexpr _Cdata_fn cdata;
} }
using namespace _Cpos;
// clang-format off // clang-format off
// CONCEPT ranges::sized_range // CONCEPT ranges::sized_range
@ -3289,9 +3305,10 @@ namespace ranges {
// clang-format on // clang-format on
}; };
inline namespace _Cpos { namespace _Cpos {
inline constexpr _Ssize_fn ssize; inline constexpr _Ssize_fn ssize;
} }
using namespace _Cpos;
// VARIABLE ranges::next // VARIABLE ranges::next
class _Next_fn : private _Not_quite_object { class _Next_fn : private _Not_quite_object {
@ -3584,7 +3601,9 @@ namespace ranges {
auto& _Self = _Cast(); auto& _Self = _Cast();
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (sized_range<_Derived>) { if constexpr (sized_range<_Derived>) {
_STL_VERIFY(_Idx < _RANGES size(_Self), "index out of range for view_interface"); using _U_diff = _Make_unsigned_like_t<range_difference_t<_Rng>>;
_STL_VERIFY(static_cast<_U_diff>(_Idx) < static_cast<_U_diff>(_RANGES size(_Self)),
"index out of range for view_interface");
} }
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
return _RANGES begin(_Self)[_Idx]; return _RANGES begin(_Self)[_Idx];
@ -3595,7 +3614,9 @@ namespace ranges {
auto& _Self = _Cast(); auto& _Self = _Cast();
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
if constexpr (sized_range<_Derived>) { if constexpr (sized_range<_Derived>) {
_STL_VERIFY(_Idx < _RANGES size(_Self), "index out of range for view_interface"); using _U_diff = _Make_unsigned_like_t<range_difference_t<_Rng>>;
_STL_VERIFY(static_cast<_U_diff>(_Idx) < static_cast<_U_diff>(_RANGES size(_Self)),
"index out of range for view_interface");
} }
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
return _RANGES begin(_Self)[_Idx]; return _RANGES begin(_Self)[_Idx];

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

@ -573,6 +573,93 @@ struct std::pointer_traits<::test::iterator<std::contiguous_iterator_tag, Elemen
namespace test { namespace test {
enum class Sized : bool { no, yes }; enum class Sized : bool { no, yes };
enum class Common : bool { no, yes }; enum class Common : bool { no, yes };
enum class CanView : bool { no, yes };
enum class Copyability { immobile, move_only, copyable };
namespace detail {
template <class Element, Copyability Copy>
class range_base {
public:
static_assert(Copy == Copyability::immobile);
range_base() = default;
constexpr explicit range_base(span<Element> elements) noexcept : elements_{elements} {}
range_base(const range_base&) = delete;
range_base& operator=(const range_base&) = delete;
protected:
[[nodiscard]] constexpr bool moved_from() const noexcept {
return false;
}
span<Element> elements_;
};
template <class Element>
class range_base<Element, Copyability::move_only> {
public:
range_base() = default;
constexpr explicit range_base(span<Element> elements) noexcept : elements_{elements} {}
constexpr range_base(range_base&& that) noexcept
: elements_{that.elements_}, moved_from_{that.moved_from_} {
that.elements_ = {};
that.moved_from_ = true;
}
constexpr range_base& operator=(range_base&& that) noexcept {
elements_ = that.elements_;
moved_from_ = that.moved_from_;
that.elements_ = {};
that.moved_from_ = true;
return *this;
}
protected:
[[nodiscard]] constexpr bool moved_from() const noexcept {
return moved_from_;
}
span<Element> elements_;
private:
bool moved_from_ = false;
};
template <class Element>
class range_base<Element, Copyability::copyable> {
public:
range_base() = default;
constexpr explicit range_base(span<Element> elements) noexcept : elements_{elements} {}
range_base(const range_base&) = default;
range_base& operator=(const range_base&) = default;
constexpr range_base(range_base&& that) noexcept
: elements_{that.elements_}, moved_from_{that.moved_from_} {
that.elements_ = {};
that.moved_from_ = true;
}
constexpr range_base& operator=(range_base&& that) noexcept {
elements_ = that.elements_;
moved_from_ = that.moved_from_;
that.elements_ = {};
that.moved_from_ = true;
return *this;
}
protected:
[[nodiscard]] constexpr bool moved_from() const noexcept {
return moved_from_;
}
span<Element> elements_;
private:
bool moved_from_ = false;
};
} // namespace detail
// clang-format off // clang-format off
template <class Category, class Element, template <class Category, class Element,
@ -585,25 +672,30 @@ namespace test {
// Iterator models sentinel_for with self // Iterator models sentinel_for with self
CanCompare Eq = CanCompare{derived_from<Category, fwd>}, CanCompare Eq = CanCompare{derived_from<Category, fwd>},
// Use a ProxyRef reference type? // Use a ProxyRef reference type?
ProxyRef Proxy = ProxyRef{!derived_from<Category, contiguous>}> ProxyRef Proxy = ProxyRef{!derived_from<Category, contiguous>},
// Should this range satisfy the view concept?
CanView IsView = CanView::no,
// Should this range type be copyable/movable/neither?
Copyability Copy = IsView == CanView::yes ? Copyability::move_only : Copyability::immobile>
requires (!to_bool(IsCommon) || to_bool(Eq)) requires (!to_bool(IsCommon) || to_bool(Eq))
&& (to_bool(Eq) || !derived_from<Category, fwd>) && (to_bool(Eq) || !derived_from<Category, fwd>)
&& (!to_bool(Proxy) || !derived_from<Category, contiguous>) && (!to_bool(Proxy) || !derived_from<Category, contiguous>)
class range { && (!to_bool(IsView) || Copy != Copyability::immobile)
span<Element> elements_; class range : public detail::range_base<Element, Copy> {
private:
mutable bool begin_called_ = false; mutable bool begin_called_ = false;
using detail::range_base<Element, Copy>::elements_;
using detail::range_base<Element, Copy>::moved_from;
public: public:
using I = iterator<Category, Element, Diff, Eq, Proxy, IsWrapped::yes>; using I = iterator<Category, Element, Diff, Eq, Proxy, IsWrapped::yes>;
using S = conditional_t<to_bool(IsCommon), I, sentinel<Element, IsWrapped::yes>>; using S = conditional_t<to_bool(IsCommon), I, sentinel<Element, IsWrapped::yes>>;
range() = default; using detail::range_base<Element, Copy>::range_base;
constexpr explicit range(span<Element> elements) noexcept : elements_{elements} {}
range(range const&) = delete;
range& operator=(range const&) = delete;
[[nodiscard]] constexpr I begin() const noexcept { [[nodiscard]] constexpr I begin() const noexcept {
assert(!moved_from());
if constexpr (!derived_from<Category, fwd>) { if constexpr (!derived_from<Category, fwd>) {
assert(!exchange(begin_called_, true)); assert(!exchange(begin_called_, true));
} }
@ -611,10 +703,12 @@ namespace test {
} }
[[nodiscard]] constexpr S end() const noexcept { [[nodiscard]] constexpr S end() const noexcept {
assert(!moved_from());
return S{elements_.data() + elements_.size()}; return S{elements_.data() + elements_.size()};
} }
[[nodiscard]] constexpr ptrdiff_t size() const noexcept requires (to_bool(IsSized)) { [[nodiscard]] constexpr ptrdiff_t size() const noexcept requires (to_bool(IsSized)) {
assert(!moved_from());
if constexpr (!derived_from<Category, fwd>) { if constexpr (!derived_from<Category, fwd>) {
assert(!begin_called_); assert(!begin_called_);
} }
@ -622,6 +716,7 @@ namespace test {
} }
[[nodiscard]] constexpr Element* data() const noexcept requires derived_from<Category, contiguous> { [[nodiscard]] constexpr Element* data() const noexcept requires derived_from<Category, contiguous> {
assert(!moved_from());
return elements_.data(); return elements_.data();
} }
@ -629,12 +724,14 @@ namespace test {
using US = conditional_t<to_bool(IsCommon), UI, sentinel<Element, IsWrapped::no>>; using US = conditional_t<to_bool(IsCommon), UI, sentinel<Element, IsWrapped::no>>;
[[nodiscard]] constexpr UI _Unchecked_begin() const noexcept { [[nodiscard]] constexpr UI _Unchecked_begin() const noexcept {
assert(!moved_from());
if constexpr (!derived_from<Category, fwd>) { if constexpr (!derived_from<Category, fwd>) {
assert(!exchange(begin_called_, true)); assert(!exchange(begin_called_, true));
} }
return UI{elements_.data()}; return UI{elements_.data()};
} }
[[nodiscard]] constexpr US _Unchecked_end() const noexcept { [[nodiscard]] constexpr US _Unchecked_end() const noexcept {
assert(!moved_from());
return US{elements_.data() + elements_.size()}; return US{elements_.data() + elements_.size()};
} }
@ -649,6 +746,11 @@ namespace test {
// clang-format on // clang-format on
} // namespace test } // namespace test
template <class Category, class Element, test::Sized IsSized, test::CanDifference Diff, test::Common IsCommon,
test::CanCompare Eq, test::ProxyRef Proxy, test::Copyability Copy>
inline constexpr bool std::ranges::enable_view<
test::range<Category, Element, IsSized, Diff, IsCommon, Eq, Proxy, test::CanView::yes, Copy>> = true;
template <class T> template <class T>
class basic_borrowed_range : public test::range<test::input, T, test::Sized::no, test::CanDifference::no, class basic_borrowed_range : public test::range<test::input, T, test::Sized::no, test::CanDifference::no,
test::Common::no, test::CanCompare::no, test::ProxyRef::no> { test::Common::no, test::CanCompare::no, test::ProxyRef::no> {
@ -1170,3 +1272,110 @@ struct get_nth_fn {
}; };
inline constexpr get_nth_fn<0> get_first; inline constexpr get_nth_fn<0> get_first;
inline constexpr get_nth_fn<1> get_second; inline constexpr get_nth_fn<1> get_second;
template <class R>
concept CanBegin = requires(R&& r) {
ranges::begin(std::forward<R>(r));
};
template <class R>
concept CanMemberBegin = requires(R&& r) {
std::forward<R>(r).begin();
};
template <class R>
concept CanEnd = requires(R&& r) {
ranges::end(std::forward<R>(r));
};
template <class R>
concept CanMemberEnd = requires(R&& r) {
std::forward<R>(r).end();
};
template <class R>
concept CanCBegin = requires(R&& r) {
ranges::cbegin(std::forward<R>(r));
};
template <class R>
concept CanCEnd = requires(R&& r) {
ranges::cend(std::forward<R>(r));
};
template <class R>
concept CanRBegin = requires(R&& r) {
ranges::rbegin(std::forward<R>(r));
};
template <class R>
concept CanREnd = requires(R&& r) {
ranges::rend(std::forward<R>(r));
};
template <class R>
concept CanCRBegin = requires(R&& r) {
ranges::crbegin(std::forward<R>(r));
};
template <class R>
concept CanCREnd = requires(R&& r) {
ranges::crend(std::forward<R>(r));
};
template <class R>
concept CanEmpty = requires(R&& r) {
ranges::empty(std::forward<R>(r));
};
template <class R>
concept CanSize = requires(R&& r) {
ranges::size(std::forward<R>(r));
};
template <class R>
concept CanMemberSize = requires(R&& r) {
std::forward<R>(r).size();
};
template <class R>
concept CanSSize = requires(R&& r) {
ranges::ssize(std::forward<R>(r));
};
template <class R>
concept CanData = requires(R&& r) {
ranges::data(std::forward<R>(r));
};
template <class R>
concept CanMemberData = requires(R&& r) {
std::forward<R>(r).data();
};
template <class R>
concept CanCData = requires(R&& r) {
ranges::cdata(std::forward<R>(r));
};
template <class T>
concept CanMemberBase = requires(T&& t) {
std::forward<T>(t).base();
};
template <class R>
concept CanMemberEmpty = requires(R&& r) {
std::forward<R>(r).empty();
};
template <class R>
concept CanMemberFront = requires(R&& r) {
std::forward<R>(r).front();
};
template <class R>
concept CanMemberBack = requires(R&& r) {
std::forward<R>(r).back();
};
template <class R>
concept CanIndex = requires(R&& r, const ranges::range_difference_t<R> i) {
std::forward<R>(r)[i];
};
template <class R>
concept CanBool = requires(R&& r) {
std::forward<R>(r) ? true : false;
};

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

@ -317,7 +317,9 @@ tests\P0896R4_ranges_ref_view
tests\P0896R4_ranges_subrange tests\P0896R4_ranges_subrange
tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_test_machinery
tests\P0896R4_ranges_to_address tests\P0896R4_ranges_to_address
tests\P0896R4_views_all
tests\P0896R4_views_empty tests\P0896R4_views_empty
tests\P0896R4_views_reverse
tests\P0896R4_views_single tests\P0896R4_views_single
tests\P0898R3_concepts tests\P0898R3_concepts
tests\P0898R3_identity tests\P0898R3_identity

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

@ -34,45 +34,10 @@ STATIC_ASSERT(std::same_as<std::make_unsigned_t<std::ptrdiff_t>, std::size_t>);
template <class T> template <class T>
concept Decayed = std::same_as<std::decay_t<T>, T>; concept Decayed = std::same_as<std::decay_t<T>, T>;
// clang-format off
template <class R> template <class R>
concept CanBegin = requires(R&& r) { ranges::begin(std::forward<R>(r)); }; concept CanSizeType = requires {
template <class R> typename ranges::range_size_t<R>;
concept CanEnd = requires(R&& r) { ranges::end(std::forward<R>(r)); }; };
template <class R>
concept CanCBegin = requires(R&& r) { ranges::cbegin(std::forward<R>(r)); };
template <class R>
concept CanCEnd = requires(R&& r) {ranges::cend(std::forward<R>(r)); };
template <class R>
concept CanRBegin = requires(R&& r) { ranges::rbegin(std::forward<R>(r)); };
template <class R>
concept CanREnd = requires(R&& r) { ranges::rend(std::forward<R>(r)); };
template <class R>
concept CanCRBegin = requires(R&& r) { ranges::crbegin(std::forward<R>(r)); };
template <class R>
concept CanCREnd = requires(R&& r) { ranges::crend(std::forward<R>(r)); };
template <class R>
concept CanEmpty = requires(R&& r) { ranges::empty(std::forward<R>(r)); };
template <class R>
concept CanSize = requires(R&& r) { ranges::size(std::forward<R>(r)); };
template <class R>
concept CanSSize = requires(R&& r) { ranges::ssize(std::forward<R>(r)); };
template <class R>
concept CanSizeType = requires { typename ranges::range_size_t<R>; };
template <class R>
concept CanData = requires(R&& r) { ranges::data(std::forward<R>(r)); };
template <class R>
concept CanCData = requires(R&& r) { ranges::cdata(std::forward<R>(r)); };
// clang-format on
struct invalid_type {}; struct invalid_type {};
@ -124,6 +89,8 @@ STATIC_ASSERT(test_cpo(ranges::empty));
STATIC_ASSERT(test_cpo(ranges::data)); STATIC_ASSERT(test_cpo(ranges::data));
STATIC_ASSERT(test_cpo(ranges::cdata)); STATIC_ASSERT(test_cpo(ranges::cdata));
STATIC_ASSERT(test_cpo(ranges::views::all));
STATIC_ASSERT(test_cpo(ranges::views::reverse));
STATIC_ASSERT(test_cpo(ranges::views::single)); STATIC_ASSERT(test_cpo(ranges::views::single));
void test_cpo_ambiguity() { void test_cpo_ambiguity() {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

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

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

@ -0,0 +1,218 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <cassert>
#include <ranges>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <range_algorithm_support.hpp>
using namespace std;
template <class Rng>
concept CanViewAll = requires(Rng&& r) {
views::all(static_cast<Rng&&>(r));
};
// Test a silly precomposed range adaptor pipeline
constexpr auto pipeline = views::all | views::all | views::all | views::all | views::all | views::all;
template <class Rng>
constexpr bool test_one(Rng&& rng) {
constexpr bool is_view = ranges::view<remove_cvref_t<Rng>>;
static_assert(is_view || is_lvalue_reference_v<Rng>);
using V = conditional_t<is_view, remove_cvref_t<Rng>, ranges::ref_view<remove_reference_t<Rng>>>;
static_assert(CanViewAll<Rng&> == (!is_view || copyable<V>) );
if constexpr (!is_view || copyable<V>) { // Validate lvalue
constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v<V>;
static_assert(same_as<views::all_t<Rng>, V>);
static_assert(same_as<decltype(views::all(rng)), V>);
static_assert(noexcept(views::all(rng)) == is_noexcept);
static_assert(same_as<decltype(rng | views::all), V>);
static_assert(noexcept(rng | views::all) == is_noexcept);
static_assert(same_as<decltype(views::all(rng)), decltype(rng | views::all | views::all | views::all)>);
static_assert(noexcept(rng | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(views::all(rng)), decltype(rng | pipeline)>);
static_assert(noexcept(rng | pipeline) == is_noexcept);
}
static_assert(CanViewAll<const remove_cvref_t<Rng>&> == (!is_view || copyable<V>) );
if constexpr (is_view && copyable<V>) {
constexpr bool is_noexcept = is_nothrow_copy_constructible_v<V>;
static_assert(same_as<views::all_t<const remove_cvref_t<Rng>&>, V>);
static_assert(same_as<decltype(views::all(as_const(rng))), V>);
static_assert(noexcept(views::all(as_const(rng))) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::all), V>);
static_assert(noexcept(as_const(rng) | views::all) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::all | views::all | views::all), V>);
static_assert(noexcept(as_const(rng) | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | pipeline), V>);
static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept);
} else if constexpr (!is_view) {
using RV = ranges::ref_view<const remove_cvref_t<Rng>>;
static_assert(same_as<views::all_t<const remove_cvref_t<Rng>&>, RV>);
static_assert(same_as<decltype(views::all(as_const(rng))), RV>);
static_assert(noexcept(views::all(as_const(rng))));
static_assert(same_as<decltype(as_const(rng) | views::all), RV>);
static_assert(noexcept(as_const(rng) | views::all));
static_assert(same_as<decltype(as_const(rng) | views::all | views::all | views::all), RV>);
static_assert(noexcept(as_const(rng) | views::all | views::all | views::all));
static_assert(same_as<decltype(as_const(rng) | pipeline), RV>);
static_assert(noexcept(as_const(rng) | pipeline));
}
// Validate rvalue
static_assert(CanViewAll<remove_cvref_t<Rng>> == is_view || ranges::enable_borrowed_range<remove_cvref_t<Rng>>);
if constexpr (is_view) {
constexpr bool is_noexcept = is_nothrow_move_constructible_v<V>;
static_assert(same_as<views::all_t<remove_cvref_t<Rng>>, V>);
static_assert(same_as<decltype(views::all(move(rng))), V>);
static_assert(noexcept(views::all(move(rng))) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::all), V>);
static_assert(noexcept(move(rng) | views::all) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::all | views::all | views::all), V>);
static_assert(noexcept(move(rng) | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(move(rng) | pipeline), V>);
static_assert(noexcept(move(rng) | pipeline) == is_noexcept);
} else if constexpr (ranges::enable_borrowed_range<Rng>) {
using S = decltype(ranges::subrange{declval<Rng>()});
constexpr bool is_noexcept = noexcept(S{declval<Rng>()});
static_assert(same_as<views::all_t<remove_cvref_t<Rng>>, S>);
static_assert(same_as<decltype(views::all(move(rng))), S>);
static_assert(noexcept(views::all(move(rng))) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::all), S>);
static_assert(noexcept(move(rng) | views::all) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::all | views::all | views::all), S>);
static_assert(noexcept(move(rng) | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(move(rng) | pipeline), S>);
static_assert(noexcept(move(rng) | pipeline) == is_noexcept);
}
// Validate const rvalue
static_assert(CanViewAll<const remove_cvref_t<Rng>> == (is_view && copyable<V>)
|| (!is_view && ranges::enable_borrowed_range<remove_cvref_t<Rng>>) );
if constexpr (is_view && copyable<V>) {
constexpr bool is_noexcept = is_nothrow_copy_constructible_v<V>;
static_assert(same_as<views::all_t<const remove_cvref_t<Rng>>, V>);
static_assert(same_as<decltype(views::all(move(as_const(rng)))), V>);
static_assert(noexcept(views::all(as_const(rng))) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::all), V>);
static_assert(noexcept(as_const(rng) | views::all) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::all | views::all | views::all), V>);
static_assert(noexcept(move(as_const(rng)) | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | pipeline), V>);
static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
} else if constexpr (!is_view && ranges::enable_borrowed_range<const remove_cvref_t<Rng>>) {
using S = decltype(ranges::subrange{declval<const remove_cvref_t<Rng>>()});
constexpr bool is_noexcept = noexcept(S{declval<const remove_cvref_t<Rng>>()});
static_assert(same_as<views::all_t<const remove_cvref_t<Rng>>, S>);
static_assert(same_as<decltype(views::all(move(as_const(rng)))), S>);
static_assert(noexcept(views::all(move(as_const(rng)))) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::all), S>);
static_assert(noexcept(move(as_const(rng)) | views::all) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::all | views::all | views::all), S>);
static_assert(noexcept(move(as_const(rng)) | views::all | views::all | views::all) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | pipeline), S>);
static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
}
return true;
}
struct non_view_borrowed_range {
int* begin() const;
int* end() const;
};
template <>
inline constexpr bool ranges::enable_borrowed_range<non_view_borrowed_range> = true;
template <class Category, test::Common IsCommon, bool is_random = derived_from<Category, random_access_iterator_tag>>
using move_only_view = test::range<Category, const int, test::Sized{is_random}, test::CanDifference{is_random},
IsCommon, test::CanCompare{derived_from<Category, forward_iterator_tag>},
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}, test::CanView::yes, test::Copyability::move_only>;
int main() {
static constexpr int some_ints[] = {0, 1, 2};
// Validate views
{ // ... copyable
constexpr string_view str{"Hello, World!"};
static_assert(test_one(str));
test_one(str);
assert(ranges::equal(views::all(str), str));
}
{ // ... move-only
test_one(move_only_view<input_iterator_tag, test::Common::no>{some_ints});
assert(ranges::equal(views::all(move_only_view<input_iterator_tag, test::Common::no>{some_ints}), some_ints));
test_one(move_only_view<forward_iterator_tag, test::Common::no>{some_ints});
assert(ranges::equal(views::all(move_only_view<forward_iterator_tag, test::Common::no>{some_ints}), some_ints));
test_one(move_only_view<forward_iterator_tag, test::Common::yes>{some_ints});
assert(
ranges::equal(views::all(move_only_view<forward_iterator_tag, test::Common::yes>{some_ints}), some_ints));
test_one(move_only_view<bidirectional_iterator_tag, test::Common::no>{some_ints});
assert(ranges::equal(
views::all(move_only_view<bidirectional_iterator_tag, test::Common::no>{some_ints}), some_ints));
test_one(move_only_view<bidirectional_iterator_tag, test::Common::yes>{some_ints});
assert(ranges::equal(
views::all(move_only_view<bidirectional_iterator_tag, test::Common::yes>{some_ints}), some_ints));
test_one(move_only_view<random_access_iterator_tag, test::Common::no>{some_ints});
assert(ranges::equal(
views::all(move_only_view<random_access_iterator_tag, test::Common::no>{some_ints}), some_ints));
test_one(move_only_view<random_access_iterator_tag, test::Common::yes>{some_ints});
assert(ranges::equal(
views::all(move_only_view<random_access_iterator_tag, test::Common::yes>{some_ints}), some_ints));
}
// Validate non-views
{
static_assert(test_one(some_ints));
test_one(some_ints);
assert(ranges::equal(views::all(some_ints), some_ints));
}
{
string str{"Hello, World!"};
test_one(str);
assert(ranges::equal(views::all(str), str));
}
// Validate a non-view borrowed range
{
constexpr span s{some_ints};
static_assert(test_one(s));
test_one(s);
}
}

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

@ -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,375 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <cassert>
#include <list>
#include <ranges>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <range_algorithm_support.hpp>
using namespace std;
template <class Rng>
concept CanViewReverse = requires(Rng&& r) {
views::reverse(static_cast<Rng&&>(r));
};
// Test a silly precomposed range adaptor pipeline
constexpr auto pipeline = views::all | views::reverse | views::all | views::reverse | views::all | views::reverse;
template <ranges::bidirectional_range Rng, class Expected>
constexpr bool test_one(Rng&& rng, Expected&& expected) {
using ranges::common_range, ranges::reverse_view, ranges::sized_range, ranges::begin, ranges::end, ranges::size,
ranges::iterator_t, ranges::range_size_t, ranges::random_access_range, ranges::prev,
ranges::enable_borrowed_range;
constexpr bool is_view = ranges::view<remove_cvref_t<Rng>>;
using V = views::all_t<Rng>;
using R = reverse_view<V>;
static_assert(ranges::view<R>);
static_assert(ranges::bidirectional_range<R>);
static_assert(random_access_range<R> == random_access_range<Rng>);
static_assert(!ranges::contiguous_range<R>);
// Validate range adapter object
// ...with lvalue argument
static_assert(CanViewReverse<Rng&> == (!is_view || copyable<V>) );
if constexpr (CanViewReverse<Rng&>) {
constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v<V>;
static_assert(same_as<decltype(views::reverse(rng)), R>);
static_assert(noexcept(views::reverse(rng)) == is_noexcept);
static_assert(same_as<decltype(rng | views::reverse), R>);
static_assert(noexcept(rng | views::reverse) == is_noexcept);
static_assert(same_as<decltype(views::reverse(views::reverse(rng))), V>);
static_assert(noexcept(views::reverse(views::reverse(rng))) == is_noexcept);
static_assert(same_as<decltype(rng | views::reverse | views::reverse | views::reverse), R>);
static_assert(noexcept(rng | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(rng | pipeline), R>);
static_assert(noexcept(rng | pipeline) == is_noexcept);
}
// ... with const lvalue argument
static_assert(CanViewReverse<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>;
static_assert(same_as<decltype(views::reverse(as_const(rng))), R>);
static_assert(noexcept(views::reverse(as_const(rng))) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::reverse), R>);
static_assert(noexcept(as_const(rng) | views::reverse) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::reverse | views::reverse | views::reverse), R>);
static_assert(noexcept(as_const(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | pipeline), R>);
static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept);
} else if constexpr (!is_view) {
using RC = reverse_view<views::all_t<const remove_reference_t<Rng>&>>;
constexpr bool is_noexcept = is_nothrow_constructible_v<RC, const remove_reference_t<Rng>&>;
static_assert(same_as<decltype(views::reverse(as_const(rng))), RC>);
static_assert(noexcept(views::reverse(as_const(rng))) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::reverse), RC>);
static_assert(noexcept(as_const(rng) | views::reverse) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | views::reverse | views::reverse | views::reverse), RC>);
static_assert(noexcept(as_const(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(as_const(rng) | pipeline), RC>);
static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept);
}
// ... with rvalue argument
static_assert(CanViewReverse<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>;
static_assert(same_as<decltype(views::reverse(move(rng))), R>);
static_assert(noexcept(views::reverse(move(rng))) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::reverse), R>);
static_assert(noexcept(move(rng) | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::reverse | views::reverse | views::reverse), R>);
static_assert(noexcept(move(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(rng) | pipeline), R>);
static_assert(noexcept(move(rng) | pipeline) == is_noexcept);
} else if constexpr (enable_borrowed_range<Rng>) {
using S = decltype(ranges::subrange{declval<Rng>()});
using RS = reverse_view<S>;
constexpr bool is_noexcept = noexcept(S{declval<Rng>()});
static_assert(same_as<decltype(views::reverse(move(rng))), RS>);
static_assert(noexcept(views::reverse(move(rng))) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::reverse), RS>);
static_assert(noexcept(move(rng) | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(rng) | views::reverse | views::reverse | views::reverse), RS>);
static_assert(noexcept(move(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(rng) | pipeline), RS>);
static_assert(noexcept(move(rng) | pipeline) == is_noexcept);
}
// ... with const rvalue argument
static_assert(CanViewReverse<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>;
static_assert(same_as<decltype(views::reverse(move(as_const(rng)))), R>);
static_assert(noexcept(views::reverse(as_const(rng))) == is_nothrow_copy_constructible_v<R>);
static_assert(same_as<decltype(move(as_const(rng)) | views::reverse), R>);
static_assert(noexcept(as_const(rng) | views::reverse) == is_nothrow_copy_constructible_v<R>);
static_assert(same_as<decltype(move(as_const(rng)) | views::reverse | views::reverse | views::reverse), R>);
static_assert(noexcept(move(as_const(rng)) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | pipeline), R>);
static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
} else if constexpr (!is_view && enable_borrowed_range<const remove_cvref_t<Rng>>) {
using S = decltype(ranges::subrange{declval<const remove_cvref_t<Rng>>()});
using RS = reverse_view<S>;
constexpr bool is_noexcept = noexcept(S{declval<const remove_cvref_t<Rng>>()});
static_assert(same_as<decltype(views::reverse(move(as_const(rng)))), RS>);
static_assert(noexcept(views::reverse(move(as_const(rng)))) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::reverse), RS>);
static_assert(noexcept(move(as_const(rng)) | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | views::reverse | views::reverse | views::reverse), RS>);
static_assert(noexcept(move(as_const(rng)) | views::reverse | views::reverse | views::reverse) == is_noexcept);
static_assert(same_as<decltype(move(as_const(rng)) | pipeline), RS>);
static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept);
}
// Validate deduction guide
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442
(void) 42;
#endif // TRANSITION, DevCom-1159442
same_as<R> auto r = reverse_view{forward<Rng>(rng)};
assert(ranges::equal(r, expected));
// Validate reverse_view::size
static_assert(CanMemberSize<R> == sized_range<Rng>);
if constexpr (sized_range<Rng>) {
assert(r.size() == static_cast<range_size_t<R>>(size(expected)));
static_assert(noexcept(r.size()) == noexcept(size(rng)));
}
static_assert(CanMemberSize<const R> == sized_range<const Rng>);
if constexpr (sized_range<const Rng>) {
assert(as_const(r).size() == static_cast<range_size_t<R>>(size(expected)));
static_assert(noexcept(r.size()) == noexcept(size(as_const(rng))));
}
// Validate view_interface::empty and operator bool
const bool is_empty = ranges::empty(expected);
assert(r.empty() == is_empty);
assert(static_cast<bool>(r) == !is_empty);
static_assert(CanMemberEmpty<const R> == common_range<Rng>);
if constexpr (common_range<Rng>) {
assert(as_const(r).empty() == is_empty);
assert(static_cast<bool>(as_const(r)) == !is_empty);
}
// Validate reverse_view::begin
static_assert(CanMemberBegin<R>);
{
// reverse_view sometimes caches begin, so let's make several extra calls
const same_as<reverse_iterator<iterator_t<V>>> auto i = r.begin();
if (!is_empty) {
assert(*i == *begin(expected));
}
assert(r.begin() == i);
assert(r.begin() == i);
// NB: non-const begin is unconditionally noexcept(false) due to caching
static_assert(!noexcept(r.begin()));
if constexpr (copyable<V>) {
auto r2 = r;
const same_as<reverse_iterator<iterator_t<V>>> auto i2 = r2.begin();
assert(r2.begin() == i2);
assert(r2.begin() == i2);
if (!is_empty) {
assert(*i2 == *i);
}
}
static_assert(CanMemberBegin<const R> == common_range<Rng>);
if constexpr (common_range<Rng>) {
const same_as<reverse_iterator<iterator_t<const V>>> auto ci = as_const(r).begin();
assert(as_const(r).begin() == ci);
assert(as_const(r).begin() == ci);
if (!is_empty) {
assert(*ci == *i);
}
static_assert(noexcept(as_const(r).begin()) == noexcept(reverse_iterator{end(as_const(rng))}));
if constexpr (copyable<V>) {
const auto r2 = r;
const same_as<reverse_iterator<iterator_t<const V>>> auto ci2 = r2.begin();
assert(r2.begin() == ci2);
assert(r2.begin() == ci2);
if (!is_empty) {
assert(*ci2 == *i);
}
}
}
}
// Validate reverse_view::end
static_assert(CanMemberEnd<R>);
if (!is_empty) {
assert(*prev(r.end()) == *prev(end(expected)));
if constexpr (copyable<V>) {
auto r2 = r;
assert(*prev(r2.end()) == *prev(end(expected)));
}
static_assert(noexcept(r.end()) == noexcept(reverse_iterator{begin(rng)}));
static_assert(CanMemberEnd<const R> == common_range<Rng>);
if constexpr (common_range<Rng>) {
assert(*prev(as_const(r).end()) == *prev(end(expected)));
static_assert(noexcept(as_const(r).end()) == noexcept(reverse_iterator{begin(as_const(rng))}));
}
}
// Validate view_interface::data
static_assert(!CanData<R>);
static_assert(!CanData<const R>);
if (!is_empty) {
// Validate view_interface::operator[]
static_assert(CanIndex<R> == random_access_range<Rng>);
static_assert(CanIndex<const R> == (random_access_range<Rng> && common_range<Rng>) );
if constexpr (random_access_range<Rng>) {
assert(r[0] == *begin(expected));
if constexpr (common_range<Rng>) {
assert(as_const(r)[0] == *begin(expected));
}
}
// Validate view_interface::front and back
assert(r.front() == *begin(expected));
assert(r.back() == *prev(end(expected)));
static_assert(CanMemberFront<const R> == common_range<Rng>);
static_assert(CanMemberBack<const R> == common_range<Rng>);
if constexpr (common_range<Rng>) {
assert(as_const(r).front() == *begin(expected));
assert(as_const(r).back() == *prev(end(expected)));
}
}
// Validate reverse_view::base() const&
static_assert(CanMemberBase<const R&> == copy_constructible<V>);
if constexpr (copy_constructible<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() == *prev(end(expected)));
if constexpr (common_range<V>) {
assert(*prev(b1.end()) == *begin(expected));
}
}
}
// Validate reverse_view::base() && (NB: do this last since it leaves r moved-from)
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442
(void) 42;
#endif // TRANSITION, DevCom-1159442
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() == *prev(end(expected)));
if constexpr (common_range<V>) {
assert(*prev(b2.end()) == *begin(expected));
}
}
return true;
}
struct instantiator {
template <ranges::bidirectional_range R>
static constexpr void call() {
R r{};
test_one(r, span<const int, 0>{});
}
};
template <class Category, test::Common IsCommon, bool is_random = derived_from<Category, random_access_iterator_tag>>
using move_only_view = test::range<Category, const int, test::Sized{is_random}, test::CanDifference{is_random},
IsCommon, test::CanCompare{derived_from<Category, forward_iterator_tag>},
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}, test::CanView::yes, test::Copyability::move_only>;
int main() {
static constexpr int some_ints[] = {0, 1, 2};
static constexpr int reversed_ints[] = {2, 1, 0};
// Validate views
{ // ...copyable
constexpr string_view str{"Hello, World!"};
constexpr auto expected = "!dlroW ,olleH"sv;
static_assert(test_one(str, expected));
test_one(str, expected);
}
{ // ... move-only
test_one(move_only_view<bidirectional_iterator_tag, test::Common::no>{some_ints}, reversed_ints);
test_one(move_only_view<bidirectional_iterator_tag, test::Common::yes>{some_ints}, reversed_ints);
test_one(move_only_view<random_access_iterator_tag, test::Common::no>{some_ints}, reversed_ints);
test_one(move_only_view<random_access_iterator_tag, test::Common::yes>{some_ints}, reversed_ints);
test_one(move_only_view<contiguous_iterator_tag, test::Common::no>{some_ints}, reversed_ints);
test_one(move_only_view<contiguous_iterator_tag, test::Common::yes>{some_ints}, reversed_ints);
}
// Validate non-views
{ // ... C array
static_assert(test_one(some_ints, reversed_ints));
test_one(some_ints, reversed_ints);
}
{ // ... contiguous container
string str{"Hello, World!"};
constexpr auto expected = "!dlroW ,olleH"sv;
test_one(str, expected);
}
{ // ... bidi container
list<int> lst{3, 4, 5};
static constexpr int reversed[] = {5, 4, 3};
test_one(lst, reversed);
static constexpr int reversed_prefix[] = {4, 3};
assert(ranges::equal(
views::reverse(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), reversed_prefix));
}
// Validate a non-view borrowed range
{
constexpr span s{some_ints};
static_assert(test_one(s, reversed_ints));
test_one(s, reversed_ints);
}
// Get full instantiation coverage
static_assert((test_bidi<instantiator, const int>(), true));
test_bidi<instantiator, const int>();
}