[libcxx-commits] [libcxx] [libc++][ranges] Implement P1899 `ranges::stride_view`. (PR #65200)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Mar 20 12:57:44 PDT 2026
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/65200
>From 27d692b423f967db06c1bdd0e720a063f6f230bc Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Tue, 30 Dec 2025 08:16:59 -0500
Subject: [PATCH 1/9] WIP: [libc++][ranges] Implement ranges::stride_view.
Differential Revision: https://reviews.llvm.org/D156924
Signed-off-by: Will Hawkins <hawkinsw at obs.cr>
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/ReleaseNotes/22.rst | 1 +
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/stride_view.h | 414 +++++++++++++++++
libcxx/include/module.modulemap.in | 4 +
libcxx/include/ranges | 12 +
libcxx/include/version | 2 +
libcxx/modules/std/ranges.inc | 4 +-
.../range.stride.view/ctor.assert.pass.cpp | 27 ++
.../iterator/dereference.pass.cpp | 34 ++
.../iterator/increment.pass.cpp | 45 ++
.../iterator/iterator.nodiscard.verify.cpp | 114 +++++
.../iterator/operator_plus_equal.pass.cpp | 26 ++
.../range.stride.view/nodiscard.verify.cpp | 73 +++
.../ranges.version.compile.pass.cpp | 30 ++
.../version.version.compile.pass.cpp | 30 ++
.../range.stride.view/adaptor.pass.cpp | 149 ++++++
.../range.stride.view/base.pass.cpp | 105 +++++
.../range.stride.view/begin.pass.cpp | 64 +++
.../borrowing.compile.pass.cpp | 19 +
.../concept.compile.pass.cpp | 41 ++
.../range.stride.view/ctad.pass.cpp | 96 ++++
.../range.stride.view/ctor.pass.cpp | 49 ++
.../range.stride.view/end.pass.cpp | 111 +++++
.../range.stride.view/iterator/base.pass.cpp | 132 ++++++
.../iterator/ctor.copy.pass.cpp | 374 +++++++++++++++
.../iterator/ctor.default.pass.cpp | 39 ++
.../iterator/decrement.pass.cpp | 93 ++++
.../range.stride.view/iterator/equal.pass.cpp | 97 ++++
.../iterator/greater_than.pass.cpp | 426 ++++++++++++++++++
.../iterator/increment.pass.cpp | 205 +++++++++
.../iterator/iter_move.pass.cpp | 97 ++++
.../iterator/iter_swap.pass.cpp | 88 ++++
.../iterator/less_than.pass.cpp | 426 ++++++++++++++++++
.../iterator/operator.pass.cpp | 426 ++++++++++++++++++
.../range.stride.view/iterator/plus.pass.cpp | 103 +++++
.../iterator/plus_equal.pass.cpp | 109 +++++
.../range.stride.view/size.pass.cpp | 54 +++
.../range.stride.view/stride.pass.cpp | 39 ++
.../range.adaptors/range.stride.view/types.h | 296 ++++++++++++
.../generate_feature_test_macro_components.py | 5 +
41 files changed, 4460 insertions(+), 2 deletions(-)
create mode 100644 libcxx/include/__ranges/stride_view.h
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..46a7231cef5d4 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -388,6 +388,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_starts_ends_with`` ``202106L``
---------------------------------------------------------- -----------------
+ ``__cpp_lib_ranges_stride`` ``202207L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_ranges_to_container`` ``202202L``
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_zip`` *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..7074dd3a503c5 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
- P3567R2: ``flat_meow`` Fixes (`Github <https://llvm.org/PR162022>`__)
- P3836R2: Make ``optional<T&>`` trivially copyable (`Github <https://llvm.org/PR171275>`__)
- P1789R3: Library Support for Expansion Statements (`Github <https://llvm.org/PR167184>`__)
+- P1899: ``stride_view`` (`Github <https://github.com/llvm/llvm-project/issues/105198>` __)
Improvements and New Features
-----------------------------
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..a913765c8c907 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -740,6 +740,7 @@ set(files
__ranges/single_view.h
__ranges/size.h
__ranges/split_view.h
+ __ranges/stride_view.h
__ranges/subrange.h
__ranges/take_view.h
__ranges/take_while_view.h
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
new file mode 100644
index 0000000000000..5ca398382948b
--- /dev/null
+++ b/libcxx/include/__ranges/stride_view.h
@@ -0,0 +1,414 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_STRIDE_VIEW_H
+#define _LIBCPP___RANGES_STRIDE_VIEW_H
+
+#include <__assert>
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/relation.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__functional/operations.h>
+#include <__functional/ranges_operations.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/indirectly_comparable.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/make_unsigned.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Value>
+_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
+ _Value __r = __left / __right;
+ if (__left % __right) {
+ ++__r;
+ }
+ return __r;
+}
+
+template <input_range _View>
+ requires view<_View>
+class stride_view : public view_interface<stride_view<_View>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ range_difference_t<_View> __stride_ = 0;
+
+ template <bool _Const>
+ class __iterator;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride)
+ : __base_(std::move(__base)), __stride_(__stride) {
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__stride > 0, "The value of stride must be greater than 0");
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_difference_t<_View> stride() const noexcept { return __stride_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return __iterator</*_Const=*/false>(this, ranges::begin(__base_), 0);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires range<const _View>
+ {
+ return __iterator</*_Const=*/true>(this, ranges::begin(__base_), 0);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View>)
+ {
+ if constexpr (common_range<_View> && sized_range<_View> && forward_range<_View>) {
+ auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+ return __iterator</*_Const=*/false>(this, ranges::end(__base_), __missing);
+ } else if constexpr (common_range<_View> && !bidirectional_range<_View>) {
+ return __iterator</*_Const=*/false>(this, ranges::end(__base_), 0);
+ } else {
+ return default_sentinel;
+ }
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires(range<const _View>)
+ {
+ if constexpr (common_range<const _View> && sized_range<const _View> && forward_range<const _View>) {
+ auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+ return __iterator</*_Const=*/true>(this, ranges::end(__base_), __missing);
+ } else if constexpr (common_range<_View> && !bidirectional_range<_View>) {
+ return __iterator</*_Const=*/true>(this, ranges::end(__base_), 0);
+ } else {
+ return default_sentinel;
+ }
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_View>
+ {
+ return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _View>
+ {
+ return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+ }
+}; // class stride_view
+
+template <class _Range>
+stride_view(_Range&&, range_difference_t<_Range>) -> stride_view<views::all_t<_Range>>;
+
+template <class _View>
+struct __stride_iterator_category {};
+
+template <forward_range _View>
+struct __stride_iterator_category<_View> {
+ using _Cat _LIBCPP_NODEBUG = typename iterator_traits<iterator_t<_View>>::iterator_category;
+ using iterator_category =
+ _If<derived_from<_Cat, random_access_iterator_tag>,
+ /* then */ random_access_iterator_tag,
+ /* else */ _Cat >;
+};
+
+template <input_range _View>
+ requires view<_View>
+template <bool _Const>
+class stride_view<_View>::__iterator : public __stride_iterator_category<_View> {
+ using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, stride_view<_View>>;
+ using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_ = iterator_t<_Base>();
+ _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
+ range_difference_t<_Base> __stride_ = 0;
+ range_difference_t<_Base> __missing_ = 0;
+
+ friend stride_view;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+ _Parent* __parent, ranges::iterator_t<_Base> __current, range_difference_t<_Base> __missing)
+ : __current_(std::move(__current)),
+ __end_(ranges::end(__parent->__base_)),
+ __stride_(__parent->__stride_),
+ __missing_(__missing) {}
+
+ static consteval auto __get_stride_view_iterator_concept() {
+ if constexpr (random_access_range<_Base>) {
+ return random_access_iterator_tag{};
+ } else if constexpr (bidirectional_range<_Base>) {
+ return bidirectional_iterator_tag{};
+ } else if constexpr (forward_range<_Base>) {
+ return forward_iterator_tag{};
+ } else {
+ return input_iterator_tag{};
+ }
+ }
+
+public:
+ using difference_type = range_difference_t<_Base>;
+ using value_type = range_value_t<_Base>;
+ using iterator_concept = decltype(__get_stride_view_iterator_concept());
+ // using iterator_category = inherited;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator()
+ requires default_initializable<iterator_t<_Base>>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
+ convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+ : __current_(std::move(__i.__current_)),
+ __end_(std::move(__i.__end_)),
+ __stride_(__i.__stride_),
+ __missing_(__i.__missing_) {}
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> const& base() const& noexcept { return __current_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot dereference an iterator at the end.");
+ return *__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end.");
+ __missing_ = ranges::advance(__current_, __stride_, __end_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end.");
+ ++*this;
+ }
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+ requires forward_range<_Base>
+ {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end.");
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires bidirectional_range<_Base>
+ {
+ ranges::advance(__current_, __missing_ - __stride_);
+ __missing_ = 0;
+ return *this;
+ }
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires bidirectional_range<_Base>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n)
+ requires random_access_range<_Base>
+ {
+ if (__n > 0) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(ranges::distance(__current_, __end_) > __stride_ * (__n - 1),
+ "Advancing the iterator beyond the end is not allowed.");
+ ranges::advance(__current_, __stride_ * (__n - 1));
+ __missing_ = ranges::advance(__current_, __stride_, __end_);
+
+ } else if (__n < 0) {
+ ranges::advance(__current_, __stride_ * __n + __missing_);
+ __missing_ = 0;
+ }
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n)
+ requires random_access_range<_Base>
+ {
+ return *this += -__n;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+ requires random_access_range<_Base>
+ {
+ return *(*this + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) {
+ return __x.__current_ == __x.__end_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+ requires equality_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ == __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base>
+ {
+ return __x.__current_ < __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base>
+ {
+ return __y < __x;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__y < __x);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__x < __y);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(__iterator const& __x, __iterator const& __y)
+ requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ <=> __y.__current_;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator const& __i, difference_type __s)
+ requires random_access_range<_Base>
+ {
+ auto __r = __i;
+ __r += __s;
+ return __r;
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __s, __iterator const& __i)
+ requires random_access_range<_Base>
+ {
+ auto __r = __i;
+ __r += __s;
+ return __r;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator const& __i, difference_type __s)
+ requires random_access_range<_Base>
+ {
+ auto __r = __i;
+ __r -= __s;
+ return __r;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(__iterator const& __x, __iterator const& __y)
+ requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+ {
+ if constexpr (forward_range<_Base>) {
+ auto __n = __x.__current_ - __y.__current_;
+ return (__n + __x.__missing_ - __y.__missing_) / __x.__stride_;
+ }
+ auto __n = __x.__current_ - __y.__current_;
+ if (__n < 0) {
+ return -ranges::__div_ceil(-__n, __x.__stride_);
+ }
+ return ranges::__div_ceil(__n, __x.__stride_);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(default_sentinel_t, __iterator const& __x)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(__iterator const& __x, default_sentinel_t __y)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return -(__y - __x);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base>
+ iter_move(__iterator const& __it) noexcept(noexcept(ranges::iter_move(__it.__current_))) {
+ return ranges::iter_move(__it.__current_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr void
+ iter_swap(__iterator const& __x,
+ __iterator const& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+ requires indirectly_swappable<iterator_t<_Base>>
+ {
+ return ranges::iter_swap(__x.__current_, __y.__current_);
+ }
+}; // class stride_view::__iterator
+
+template <class _Tp>
+inline constexpr bool enable_borrowed_range<stride_view<_Tp>> = enable_borrowed_range<_Tp>;
+
+namespace views {
+namespace __stride_view {
+struct __fn {
+ // clang-format off
+ template <viewable_range _Range>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const
+ noexcept(noexcept(stride_view{std::forward<_Range>(__range), __n}))
+ -> decltype( stride_view{std::forward<_Range>(__range), __n})
+ { return stride_view(std::forward<_Range>(__range), __n); }
+ // clang-format on
+
+ template <class _Np>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const {
+ return __pipeable(std::__bind_back(*this, std::forward<_Np>(__n)));
+ }
+};
+} // namespace __stride_view
+
+inline namespace __cpo {
+inline constexpr auto stride = __stride_view::__fn{};
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_STRIDE_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..3ea811b3e5438 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1926,6 +1926,10 @@ module std [system] {
module subrange_fwd {
header "__fwd/subrange.h"
}
+ module stride_view {
+ header "__ranges/stride_view.h"
+ export std.functional.bind_back
+ }
module take_view {
header "__ranges/take_view.h"
export std.functional.bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..e48bcc87c5952 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
class chunk_by_view; // C++23
namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23
+
+ // [range.stride.view], stride view
+ template<input_range V>
+ requires view<V>
+ class stride_view; // C++23
+
+ template<class V>
+ constexpr bool enable_borrowed_range<stride_view<V>> =
+ enable_borrowed_range<V>; // C++23
+
+ namespace views { inline constexpr unspecified stride = unspecified; } // C++23
}
namespace std {
@@ -485,6 +496,7 @@ namespace std {
# include <__ranges/from_range.h>
# include <__ranges/join_with_view.h>
# include <__ranges/repeat_view.h>
+# include <__ranges/stride_view.h>
# include <__ranges/to.h>
# include <__ranges/zip_transform_view.h>
# include <__ranges/zip_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..2fb1ad8b4c2c1 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -216,6 +216,7 @@ __cpp_lib_ranges_join_with 202202L <ranges>
__cpp_lib_ranges_repeat 202207L <ranges>
__cpp_lib_ranges_slide 202202L <ranges>
__cpp_lib_ranges_starts_ends_with 202106L <algorithm>
+__cpp_lib_ranges_stride 202207L <ranges>
__cpp_lib_ranges_to_container 202202L <ranges>
__cpp_lib_ranges_zip 202110L <ranges> <tuple> <utility>
__cpp_lib_ratio 202306L <ratio>
@@ -532,6 +533,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
# define __cpp_lib_ranges_starts_ends_with 202106L
+# define __cpp_lib_ranges_stride 202207L
# define __cpp_lib_ranges_to_container 202202L
// # define __cpp_lib_ranges_zip 202110L
// # define __cpp_lib_reference_from_temporary 202202L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..c18efe6bf4af4 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -340,16 +340,16 @@ export namespace std {
namespace views {
using std::ranges::views::chunk_by;
}
-#endif // _LIBCPP_STD_VER >= 23
-#if 0
// [range.stride], stride view
using std::ranges::stride_view;
namespace views {
using std::ranges::views::stride;
}
+#endif // _LIBCPP_STD_VER >= 23
+#if 0
using std::ranges::cartesian_product_view;
namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
new file mode 100644
index 0000000000000..2b5a549b72d4e
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// constexpr explicit stride_view(_View, range_difference_t<_View>)
+
+#include <ranges>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ int range[] = {1, 2, 3};
+ TEST_LIBCPP_ASSERT_FAILURE(
+ [&range] { std::ranges::stride_view sv(range, 0); }(), "The value of stride must be greater than 0");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ [&range] { std::ranges::stride_view sv(range, -1); }(), "The value of stride must be greater than 0");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
new file mode 100644
index 0000000000000..98d62eeef8f26
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
+// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// constexpr decltype(auto) operator*() const
+
+#include "check_assertion.h"
+#include <ranges>
+
+int main(int, char**) {
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 3);
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end.");
+ }
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 4);
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end.");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..bc328b9bf92bc
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
+// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// constexpr stride_view::<iterator>& operator++()
+// constexpr __iterator& operator++()
+// constexpr void operator++(int) {
+// constexpr __iterator operator++(int)
+
+#include <ranges>
+
+#include "check_assertion.h"
+#include "test_iterators.h"
+
+#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h"
+
+int main(int, char**) {
+ {
+ int range[] = {1, 2, 3};
+ using Base = BasicTestView<cpp17_input_iterator<int*>>;
+ auto view = std::ranges::views::stride(Base(cpp17_input_iterator(range), cpp17_input_iterator(range + 3)), 3);
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
+ TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
+ }
+ {
+ int range[] = {1, 2, 3};
+ using Base = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto view = std::ranges::views::stride(Base(forward_iterator(range), forward_iterator(range + 3)), 3);
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
+ TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..70fa3799f48c5
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// Test that stride_view's iterator member functions are properly marked nodiscard.
+
+#include <ranges>
+#include <utility>
+
+#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h"
+
+void test_base_nodiscard() {
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 3);
+ auto it = view.begin();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ it.base();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::move(it).base();
+ }
+}
+
+void test_dereference_nodiscard() {
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 3);
+ auto it = view.begin();
+ ++it;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ *std::as_const(it);
+ }
+}
+
+constexpr bool test_iter_move_nodiscard() {
+ {
+ int a[] = {4, 3, 2, 1};
+
+ int iter_move_counter(0);
+ using View = IterMoveIterSwapTestRange<int*, true, true>;
+ using StrideView = std::ranges::stride_view<View>;
+ auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
+
+ static_assert(std::is_same_v<int, decltype(std::ranges::iter_move(svb))>);
+ static_assert(noexcept(std::ranges::iter_move(svb)));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::iter_move(svb);
+ }
+ return true;
+}
+
+constexpr bool test_non_forward_operator_minus_nodiscard() {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ using Base = BasicTestView<SizedInputIter, SizedInputIter>;
+
+ auto base_view_offset_zero = Base(SizedInputIter(arr), SizedInputIter(arr + 10));
+ auto base_view_offset_one = Base(SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv_one_offset_begin - sv_zero_offset_begin;
+ return true;
+}
+
+constexpr bool test_forward_operator_minus_nodiscard() {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ using Base = BasicTestView<int*, int*>;
+
+ auto base_view_offset_zero = Base(arr, arr + 10);
+ auto base_view_offset_one = Base(arr + 1, arr + 10);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv_zero_offset_begin + 1;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ 1 + sv_zero_offset_begin;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv_one_offset_begin - 1;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv_one_offset_begin - sv_zero_offset_begin;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::default_sentinel_t() - sv_zero_offset_begin;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv_zero_offset_begin - std::default_sentinel_t();
+
+ return true;
+}
+
+void test_subscript_nodiscard() {
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 3);
+ auto it = view.begin();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ it[0];
+ }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp
new file mode 100644
index 0000000000000..710ff20efe66e
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
+// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// constexpr __iterator& operator+=(difference_type __n)
+
+#include <ranges>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 2);
+ auto it = view.begin();
+ TEST_LIBCPP_ASSERT_FAILURE(it += 3, "Advancing the iterator beyond the end is not allowed.");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..ae1e154449617
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// Test that
+// std::view::stride()
+// std::ranges::stride_view::base()
+// std::ranges::stride_view::begin()
+// std::ranges::stride_view::end()
+// std::ranges::stride_view::size()
+// std::ranges::stride_view::stride()
+// are all marked nodiscard.
+
+#include <ranges>
+
+#include "../../../../std/ranges/range.adaptors/range.stride.view/types.h"
+
+void test_base_nodiscard() {
+ const int range[] = {1, 2, 3};
+ auto sv = std::ranges::stride_view(range, 3);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv.base();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::move(sv).base();
+}
+
+void test_begin_nodiscard() {
+ const auto const_sv = std::views::stride(SimpleCommonConstView{}, 2);
+ auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ const_sv.begin();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ unsimple_sv.begin();
+}
+
+void test_views_stride_nodiscard() {
+ const int range[] = {1, 2, 3};
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::stride(range, 2);
+}
+
+void test_end_nodiscard() {
+ const int range[] = {1, 2, 3};
+
+ const auto const_sv = std::views::stride(range, 2);
+ auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ const_sv.end();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ unsimple_sv.end();
+}
+
+void test_size_nodiscard() {
+ auto sv = std::views::stride(SimpleNoConstSizedCommonView(), 2);
+ const auto const_sv = std::views::stride(SimpleCommonConstView(), 2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ sv.size();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ const_sv.size();
+}
+
+void test_stride_nodiscard() {
+ const int range[] = {1, 2, 3};
+ auto const_sv = std::views::stride(range, 2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ const_sv.stride();
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..8d69867feba8f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -64,6 +64,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -118,6 +122,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -172,6 +180,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -229,6 +241,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -325,6 +341,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+# error "__cpp_lib_ranges_stride should have the value 202207L in c++23"
+# endif
+
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
@@ -454,6 +477,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+# error "__cpp_lib_ranges_stride should have the value 202207L in c++26"
+# endif
+
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index b3b424a1d77ce..1287acb94c057 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -696,6 +696,10 @@
# error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -1652,6 +1656,10 @@
# error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -2779,6 +2787,10 @@
# error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -4173,6 +4185,10 @@
# error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
@@ -5783,6 +5799,13 @@
# error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++23"
# endif
+# ifndef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+# error "__cpp_lib_ranges_stride should have the value 202207L in c++23"
+# endif
+
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
@@ -7714,6 +7737,13 @@
# error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++26"
# endif
+# ifndef __cpp_lib_ranges_stride
+# error "__cpp_lib_ranges_stride should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+# error "__cpp_lib_ranges_stride should have the value 202207L in c++26"
+# endif
+
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
new file mode 100644
index 0000000000000..fd4497254275e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// std::views::stride_view
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+constexpr BasicTestView<cpp17_input_iterator<int*>> make_input_view(int* begin, int* end) {
+ return BasicTestView<cpp17_input_iterator<int*>>(cpp17_input_iterator<int*>(begin), cpp17_input_iterator<int*>(end));
+}
+
+using ForwardStrideView = std::ranges::stride_view<BasicTestView<forward_iterator<int*>>>;
+using BidirStrideView = std::ranges::stride_view<BasicTestView<bidirectional_iterator<int*>>>;
+using RandomAccessStrideView = std::ranges::stride_view<BasicTestView<random_access_iterator<int*>>>;
+
+using SizedForwardStrideView =
+ std::ranges::stride_view<BasicTestView<random_access_iterator<int*>, random_access_iterator<int*>>>;
+using SizedInputStrideView = std::ranges::stride_view<BasicTestView<SizedInputIter, SizedInputIter>>;
+
+static_assert(std::ranges::forward_range<ForwardStrideView>);
+static_assert(std::ranges::bidirectional_range<BidirStrideView>);
+static_assert(std::ranges::random_access_range<RandomAccessStrideView>);
+static_assert(std::ranges::forward_range<SizedForwardStrideView>);
+static_assert(std::sized_sentinel_for<std::ranges::iterator_t<SizedForwardStrideView>,
+ std::ranges::iterator_t<SizedForwardStrideView>>);
+static_assert(std::sized_sentinel_for<std::ranges::iterator_t<SizedInputStrideView>,
+ std::ranges::iterator_t<SizedInputStrideView>>);
+
+constexpr bool test() {
+ constexpr int N = 3;
+ int arr[N] = {1, 2, 3};
+
+ // Test that `std::views::stride` is a range adaptor.
+ // Check various forms of
+
+ // view | stride
+ // two tests: first with stride of 1; second with stride of 2.
+ {
+ using View = BasicTestView<cpp17_input_iterator<int*>>;
+ auto view = make_input_view(arr, arr + N);
+ std::same_as<std::ranges::stride_view<View>> decltype(auto) strided = view | std::views::stride(1);
+ auto strided_iter = strided.begin();
+
+ // Check that the begin() iter views arr[0]
+ assert(*strided_iter == arr[0]);
+
+ // Check that the strided_iter, after advancing it 2 * 1 steps, views arr[2].
+ std::ranges::advance(strided_iter, 2);
+ assert(*strided_iter == arr[2]);
+ }
+ {
+ using View = BasicTestView<cpp17_input_iterator<int*>>;
+ auto view = make_input_view(arr, arr + N);
+ std::same_as<std::ranges::stride_view<View>> decltype(auto) strided = view | std::views::stride(2);
+ auto strided_iter = strided.begin();
+
+ assert(*strided_iter == arr[0]);
+ std::ranges::advance(strided_iter, 1);
+ assert(*strided_iter == arr[2]);
+ }
+
+ // adaptor | stride
+ // two tests: first with stride of 1; second with stride of 2.
+ const auto i2 = [](int i) { return i * 2; };
+ {
+ auto view = make_input_view(arr, arr + N);
+ const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(1);
+
+ auto transform_stride_applied = transform_stride_partial(view);
+ auto transform_stride_applied_iter = transform_stride_applied.begin();
+
+ assert(*transform_stride_applied_iter == i2(arr[0]));
+ std::ranges::advance(transform_stride_applied_iter, 2);
+ assert(*transform_stride_applied_iter == i2(arr[2]));
+ }
+
+ {
+ auto view = make_input_view(arr, arr + N);
+ const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(2);
+
+ const auto transform_stride_applied = transform_stride_partial(view);
+ auto transform_stride_applied_iter = transform_stride_applied.begin();
+
+ assert(*transform_stride_applied_iter == i2(arr[0]));
+ std::ranges::advance(transform_stride_applied_iter, 1);
+ assert(*transform_stride_applied_iter == i2(arr[2]));
+ }
+
+ // stride | adaptor
+ // two tests: first with stride of 1; second with stride of 2.
+ {
+ auto view = make_input_view(arr, arr + N);
+ const auto stride_transform = std::views::stride(view, 1) | std::views::transform(i2);
+
+ auto stride_transform_iter = stride_transform.begin();
+
+ assert(*stride_transform_iter == i2(arr[0]));
+ std::ranges::advance(stride_transform_iter, 2);
+ assert(*stride_transform_iter == i2(arr[2]));
+ }
+ {
+ auto view = make_input_view(arr, arr + N);
+ const auto stride_transform = std::views::stride(view, 2) | std::views::transform(i2);
+
+ auto stride_transform_iter = stride_transform.begin();
+
+ assert(*stride_transform_iter == i2(arr[0]));
+ std::ranges::advance(stride_transform_iter, 1);
+ assert(*stride_transform_iter == i2(arr[2]));
+ }
+
+ // Check SFINAE friendliness
+ {
+ struct NotAViewableRange {};
+ using View = BasicTestView<bidirectional_iterator<int*>>;
+
+ static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::stride), NotAViewableRange, int>);
+
+ static_assert(CanBePiped<View, decltype(std::views::stride(5))>);
+ static_assert(CanBePiped<View&, decltype(std::views::stride(5))>);
+ static_assert(!CanBePiped<NotAViewableRange, decltype(std::views::stride(5))>);
+ static_assert(!CanBePiped<View&, decltype(std::views::stride(NotAViewableRange{}))>);
+ }
+
+ // A final sanity check.
+ {
+ static_assert(std::same_as<decltype(std::views::stride), decltype(std::ranges::views::stride)>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
new file mode 100644
index 0000000000000..4a9c4647e928a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr _View base() const& requires copy_constructible<_View>;
+// constexpr _View base() &&;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <typename T>
+constexpr bool hasLValueQualifiedBase(T&& t) {
+ // Thanks to forwarding references, t's type is
+ // preserved from the caller. No matter the type of
+ // the argument, when it is used here, t is an l value
+ // (after all, it has a name). Therefore, this code
+ // will test whether the l value const-ref-qualified
+ // version of base is callable.
+ return requires { t.base(); };
+}
+
+using CopyableInputView = CopyableView<cpp17_input_iterator<int*>>;
+using MoveOnlyInputView = MoveOnlyView<cpp17_input_iterator<int*>>;
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ constexpr int N = 8;
+
+ // l-value ref qualified
+ // const &
+ {
+ const auto str(std::ranges::stride_view<CopyableInputView>(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+
+ static_assert(hasLValueQualifiedBase(str));
+
+ std::same_as<CopyableInputView> decltype(auto) s = str.base();
+ assert(*s.begin() == *buff);
+ }
+
+ // &
+ {
+ auto str(std::ranges::stride_view<CopyableInputView>(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+
+ std::same_as<CopyableInputView> decltype(auto) s = str.base();
+ assert(*s.begin() == *buff);
+
+ static_assert(hasLValueQualifiedBase(str));
+ }
+
+ // r-value ref qualified
+ // &&
+ {
+ auto str(std::ranges::stride_view<CopyableInputView>(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+
+ static_assert(hasLValueQualifiedBase(str));
+
+ std::same_as<CopyableInputView> decltype(auto) s = std::move(str).base();
+ assert(*s.begin() == *buff);
+ }
+
+ // const &&
+ {
+ const auto str_a(std::ranges::stride_view<CopyableInputView>(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+
+ static_assert(hasLValueQualifiedBase(str_a));
+
+ std::same_as<CopyableInputView> decltype(auto) s = std::move(str_a).base();
+ assert(*s.begin() == *buff);
+ }
+
+ // &&
+ {
+ auto str(std::ranges::stride_view<MoveOnlyInputView>(
+ MoveOnlyInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+
+ // Because the base of the stride view is move only,
+ // the const & version is not applicable and, therefore,
+ // there is no l-value qualified base method.
+ static_assert(!hasLValueQualifiedBase(str));
+
+ std::same_as<MoveOnlyInputView> decltype(auto) s = std::move(str).base();
+ assert(*s.begin() == *buff);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
new file mode 100644
index 0000000000000..d892e11fc7a8b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr auto begin() requires(!__simple_view<_View>)
+// constexpr auto begin() const requires range<const _View>
+
+// Note: Checks here are augmented by checks in
+// iterator/ctor.copy.pass.cpp.
+
+#include <concepts>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept HasConstBegin = requires(const T& ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T& t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin = requires(T& t, const T& ct) {
+ // The return types for begin are different when this is const or not:
+ // the iterator's _Const non-type-template parameter is true in the former
+ // and false in the latter.
+ requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>;
+};
+
+template <class T>
+// There is a begin but it's not const qualified => Only non-const qualified begin.
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+// There is a const-qualified begin and there are not both const- and non-const qualified begin => Only const-qualified begin.
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+static_assert(HasOnlyNonConstBegin<std::ranges::stride_view<UnSimpleNoConstCommonView>>);
+static_assert(HasOnlyConstBegin<std::ranges::stride_view<SimpleCommonConstView>>);
+static_assert(HasConstAndNonConstBegin<std::ranges::stride_view<UnSimpleConstView>>);
+
+constexpr bool test() {
+ const auto unsimple_const_view = UnSimpleConstView();
+ const auto sv_unsimple_const = std::ranges::stride_view(unsimple_const_view, 1);
+ static_assert(std::same_as<decltype(*sv_unsimple_const.begin()), double&>);
+
+ auto simple_const_view = SimpleCommonConstView();
+ auto sv_simple_const = std::ranges::stride_view(simple_const_view, 1);
+ static_assert(std::same_as<decltype(*sv_simple_const.begin()), int&>);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..4846f728c7011
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// template<class V>
+// inline constexpr bool enable_borrowed_range<stride_view<V>> = ranges::enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+
+static_assert(std::ranges::enable_borrowed_range< std::ranges::stride_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range< std::ranges::stride_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..ea60d282c422e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// template <input_range _View> requires view<_View>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <typename I, std::ranges::range_difference_t<I> D>
+concept CanStrideView = requires { std::ranges::stride_view<I>{I{}, D}; };
+
+// Ensure that the InputRangeNotIndirectlyReadable is a valid range.
+static_assert(std::ranges::range<InputRangeNotIndirectlyReadable>);
+// Ensure that the InputRangeNotIndirectlyReadable's is not an input range ...
+static_assert(!std::ranges::input_range<std::ranges::iterator_t<InputRangeNotIndirectlyReadable>>);
+// Because CanStrideView requires that the range/view type be default constructible, let's double check that ...
+static_assert(std::is_constructible_v<InputRangeNotIndirectlyReadable>);
+// And now, finally, let's make sure that we cannot stride over a range whose iterator is not an input iterator ...
+static_assert(!CanStrideView<InputRangeNotIndirectlyReadable, 1>);
+
+// Ensure that a range that is not a view cannot be the subject of a stride_view.
+static_assert(std::ranges::range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::movable<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanStrideView<test_non_const_range<cpp17_input_iterator>, 1>);
+
+// And now, let's satisfy all the prerequisites and make sure that we can stride over a range (that is an input range
+// and is a view!)
+static_assert(std::ranges::range<test_view<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_view<cpp17_input_iterator>>);
+static_assert(std::ranges::view<test_view<cpp17_input_iterator>>);
+static_assert(CanStrideView<test_view<cpp17_input_iterator>, 1>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
new file mode 100644
index 0000000000000..f0f1943de1a2c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// template <class _Range>
+// stride_view(_Range&&, range_difference_t<_Range>) -> stride_view<views::all_t<_Range>>;
+
+#include "types.h"
+#include <concepts>
+#include <ranges>
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Range {
+ int* begin() const;
+ int* end() const;
+};
+
+constexpr bool testCTAD() {
+ int a[] = {1, 2, 3, 4, 5};
+
+ using BaseRange = BasicTestRange<cpp17_input_iterator<int*>>;
+ using BaseView = BasicTestView<int*>;
+
+ auto base_view = BaseView(a, a + 5);
+ auto base_view_for_move = BaseView(a, a + 5);
+
+ auto base_range = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
+ auto base_range_for_move = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
+
+ auto copied_stride_base_view = std::ranges::stride_view(base_view, 2);
+ auto moved_stride_base_view = std::ranges::stride_view(std::move(base_view_for_move), 2);
+
+ auto copied_stride_base_range = std::ranges::stride_view(base_range, 2);
+ auto moved_stride_base_range = std::ranges::stride_view(std::move(base_range_for_move), 2);
+
+ static_assert(std::same_as< decltype(copied_stride_base_view), std::ranges::stride_view<BaseView>>);
+ static_assert(std::same_as< decltype(moved_stride_base_view), std::ranges::stride_view<BaseView>>);
+
+ static_assert(
+ std::same_as< decltype(copied_stride_base_range), std::ranges::stride_view<std::ranges::ref_view<BaseRange>> >);
+ static_assert(
+ std::same_as< decltype(moved_stride_base_range), std::ranges::stride_view<std::ranges::owning_view<BaseRange>> >);
+
+ assert(*copied_stride_base_range.begin() == 1);
+ assert(*moved_stride_base_range.begin() == 1);
+
+ assert(*copied_stride_base_view.begin() == 1);
+ assert(*moved_stride_base_view.begin() == 1);
+
+ auto copied_stride_range_it = copied_stride_base_range.begin();
+ copied_stride_range_it++;
+ assert(*copied_stride_range_it == 3);
+ copied_stride_range_it++;
+ copied_stride_range_it++;
+ assert(copied_stride_range_it == copied_stride_base_range.end());
+
+ auto moved_stride_range_it = moved_stride_base_range.begin();
+ moved_stride_range_it++;
+ moved_stride_range_it++;
+ assert(*moved_stride_range_it == 5);
+ moved_stride_range_it++;
+ assert(moved_stride_range_it == moved_stride_base_range.end());
+
+ auto copied_stride_view_it = copied_stride_base_view.begin();
+ copied_stride_view_it++;
+ assert(*copied_stride_view_it == 3);
+ copied_stride_view_it++;
+ copied_stride_view_it++;
+ assert(copied_stride_view_it == copied_stride_base_view.end());
+
+ auto moved_stride_view_it = copied_stride_base_view.begin();
+ moved_stride_view_it++;
+ moved_stride_view_it++;
+ assert(*moved_stride_view_it == 5);
+ moved_stride_view_it++;
+ assert(moved_stride_view_it == moved_stride_base_view.end());
+
+ return true;
+}
+
+int main(int, char**) {
+ testCTAD();
+ static_assert(testCTAD());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
new file mode 100644
index 0000000000000..6b4859499d076
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr explicit stride_view(_View, range_difference_t<_View>)
+
+#include <type_traits>
+
+#include "test_convertible.h"
+#include "test_iterators.h"
+#include "types.h"
+
+// There is no default ctor for stride_view.
+using View = BasicTestView<cpp17_input_iterator<int*>>;
+static_assert(!std::is_default_constructible_v<std::ranges::stride_view<View>>);
+
+// Test that the stride_view can only be explicitly constructed.
+static_assert(!test_convertible<std::ranges::stride_view<View>, View, int>());
+
+constexpr bool test() {
+ {
+ int arr[] = {1, 2, 3};
+ // Test that what we will stride over is move only.
+ static_assert(!std::is_copy_constructible_v<MoveOnlyView<cpp17_input_iterator<int*>>>);
+ static_assert(std::is_move_constructible_v<MoveOnlyView<cpp17_input_iterator<int*>>>);
+
+ MoveOnlyView<cpp17_input_iterator<int*>> mov(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3));
+ // Because MoveOnlyView is, well, move only, we can test that it is moved
+ // from when the stride view is constructed.
+ std::ranges::stride_view<MoveOnlyView<cpp17_input_iterator<int*>>> mosv(std::move(mov), 1);
+
+ // While we are here, make sure that the ctor captured the stride.
+ assert(mosv.stride() == 1);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
new file mode 100644
index 0000000000000..af6532f790a6a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr auto end() requires(!__simple_view<_View>)
+// constexpr auto end() const requires(range<const _View>)
+
+// Note: Checks here are augmented by checks in
+// iterator/ctor.copy.pass.cpp.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class T>
+concept HasConstEnd = requires(const T& ct) { ct.end(); };
+
+template <class T>
+concept HasEnd = requires(T& t) { t.end(); };
+
+template <class T>
+concept HasConstAndNonConstEnd =
+ HasConstEnd<T> && requires(T& t, const T& ct) { requires !std::same_as<decltype(t.end()), decltype(ct.end())>; };
+
+template <class T>
+concept HasOnlyNonConstEnd = HasEnd<T> && !HasConstEnd<T>;
+
+template <class T>
+concept HasOnlyConstEnd = HasConstEnd<T> && !HasConstAndNonConstEnd<T>;
+
+static_assert(HasOnlyNonConstEnd<std::ranges::stride_view<UnSimpleNoConstCommonView>>);
+static_assert(HasOnlyConstEnd<std::ranges::stride_view<SimpleCommonConstView>>);
+static_assert(HasConstAndNonConstEnd<std::ranges::stride_view<UnSimpleConstView>>);
+
+constexpr bool test_non_default_sentinel() {
+ {
+ const int data[3] = {1, 2, 3};
+ // A const, simple, common-, sized- and forward-range
+ // Note: sized because it is possible to get a difference between its
+ // beginning and its end.
+ auto v = BasicTestView<const int*, const int*>{data, data + 3};
+ auto sv = std::ranges::stride_view<BasicTestView<const int*, const int*>>(v, 1);
+ static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
+ }
+
+ {
+ int data[3] = {1, 2, 3};
+ // ForwardTestView is not sized and not bidirectional, but it is common.
+ // Note: It is not sized because BasicTestView has no member function named size (by default)
+ // and nor is it possible to get a difference between its beginning and its end.
+ using ForwardTestView = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+
+ auto v = ForwardTestView{forward_iterator(data), forward_iterator(data + 3)};
+ auto sv = std::ranges::stride_view<ForwardTestView>(v, 1);
+ static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
+ }
+
+ {
+ // TODO: Start here.
+ static_assert(!simple_view<UnSimpleNoConstCommonView>);
+ static_assert(std::ranges::common_range<UnSimpleNoConstCommonView>);
+ static_assert(std::ranges::sized_range<UnSimpleNoConstCommonView>);
+ static_assert(std::ranges::forward_range<UnSimpleNoConstCommonView>);
+
+ // An unconst, unsimple, common-, sized- and forward-range
+ auto v = UnSimpleNoConstCommonView{};
+ auto sv = std::ranges::stride_view<UnSimpleNoConstCommonView>(v, 1);
+ static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
+ }
+ return true;
+}
+
+constexpr bool test_default_sentinel() {
+ {
+ static_assert(!simple_view<UnsimpleUnCommonConstView>);
+ static_assert(!std::ranges::common_range<UnsimpleUnCommonConstView>);
+ static_assert(std::ranges::sized_range<UnSimpleConstView>);
+ static_assert(std::ranges::forward_range<UnSimpleConstView>);
+
+ auto v = UnsimpleUnCommonConstView{};
+ auto sv = std::ranges::stride_view<UnsimpleUnCommonConstView>(v, 1);
+ ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end()));
+ }
+
+ {
+ static_assert(simple_view<SimpleUnCommonConstView>);
+ static_assert(!std::ranges::common_range<SimpleUnCommonConstView>);
+
+ auto v = SimpleUnCommonConstView{};
+ auto sv = std::ranges::stride_view<SimpleUnCommonConstView>(v, 1);
+
+ ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end()));
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test_non_default_sentinel();
+ test_default_sentinel();
+ static_assert(test_non_default_sentinel());
+ static_assert(test_default_sentinel());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..f8c6725b2e057
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr iterator_t<_Base> const& base() const& noexcept
+// constexpr iterator_t<_Base> base() &&
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+constexpr bool base_noexcept() {
+ {
+ int arr[] = {1, 2, 3};
+ auto stride = std::ranges::stride_view(arr, 1);
+ [[maybe_unused]] auto stride_iter = stride.begin();
+
+ // Check that calling base on an iterator where this is an lvalue reference
+ // is noexcept.
+ static_assert(noexcept(stride_iter.base()));
+ // Calling base on an iterator where this is an rvalue reference may except.
+ static_assert(!noexcept((std::move(stride_iter).base())));
+ }
+
+ return true;
+}
+
+constexpr bool base_const() {
+ {
+ int arr[] = {1, 2, 3};
+ auto stride = std::ranges::stride_view(arr, 1);
+ [[maybe_unused]] auto stride_iter = stride.begin();
+
+ // Calling base on an iterator where this is lvalue returns a const ref to an iterator.
+ static_assert(std::is_const_v<std::remove_reference_t<decltype(stride_iter.base())>>);
+ // Calling base on an iterator where this is an rvalue reference returns a non-const iterator.
+ static_assert(!std::is_const_v<decltype(std::move(stride_iter).base())>);
+ }
+
+ return true;
+}
+
+constexpr bool base_move() {
+ // Keep track of how many times the original iterator is moved
+ // and/or copied during the test.
+ int move_counter = 0;
+ int copy_counter = 0;
+
+ auto start = SizedInputIter();
+ start.move_counter = &move_counter;
+ start.copy_counter = ©_counter;
+ auto stop = SizedInputIter();
+
+ auto view = BasicTestView<SizedInputIter>{start, stop};
+ assert(move_counter == 0);
+ // One copies of _start_ occurs when it is copied to the basic test view's member variable.
+ assert(copy_counter == 1);
+
+ auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
+ // There is a copy of _view_ made when it is passed by value.
+ // There is a move done of _view_ when it is used as the initial value of __base.
+ assert(move_counter == 1);
+ assert(copy_counter == 2);
+
+ auto svi = sv.begin();
+ // Another copy of _start_ when begin uses the iterator to the first element
+ // of the view underlying sv as the by-value parameter to the stride view iterator's
+ // constructor.
+ assert(copy_counter == 3);
+ // Another move of __start_ happens right after that when it is std::move'd to
+ // become the first __current of the iterator.
+ assert(move_counter == 2);
+
+ [[maybe_unused]] auto result = std::move(svi).base();
+ // Ensure that base std::move'd the iterator and did not copy it.
+ assert(move_counter == 3);
+ assert(copy_counter == 3);
+ return true;
+}
+
+constexpr bool base_copy() {
+ // See base_move() for complete description of when/why
+ // moves/copies take place..
+ int move_counter = 0;
+ int copy_counter = 0;
+ auto start = SizedInputIter();
+
+ start.move_counter = &move_counter;
+ start.copy_counter = ©_counter;
+ auto stop = SizedInputIter();
+
+ auto view = BasicTestView<SizedInputIter>{start, stop};
+ assert(move_counter == 0);
+ assert(copy_counter == 1);
+
+ auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
+ assert(move_counter == 1);
+ assert(copy_counter == 2);
+
+ [[maybe_unused]] auto svi = sv.begin();
+ assert(copy_counter == 3);
+ assert(move_counter == 2);
+
+ [[maybe_unused]] const SizedInputIter result = svi.base();
+ // Ensure that base did _not_ std::move'd the iterator.
+ assert(move_counter == 2);
+ assert(copy_counter == 4);
+
+ return true;
+}
+
+int main(int, char**) {
+ base_noexcept();
+ static_assert(base_noexcept());
+
+ base_const();
+ static_assert(base_const());
+
+ base_move();
+ static_assert(base_move());
+
+ base_copy();
+ static_assert(base_copy());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
new file mode 100644
index 0000000000000..8667a581d5d51
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
@@ -0,0 +1,374 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator(__iterator<!_Const> __i)
+// requires _Const && convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
+// convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+
+#include <ranges>
+
+#include "../types.h"
+#include "test_macros.h"
+
+struct NotSimpleViewIterEnd;
+template <bool, bool>
+struct NotSimpleViewConstIterEnd;
+template <bool, bool>
+struct NotSimpleViewConstIterBegin;
+
+struct NotSimpleViewIterBegin : InputIter<NotSimpleViewIterBegin> {
+ template <bool CopyConvertible, bool MoveConvertible>
+ friend constexpr bool
+ operator==(const NotSimpleViewIterBegin&, const NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible>&) {
+ return true;
+ }
+ template <bool CopyConvertible, bool MoveConvertible>
+ friend constexpr bool
+ operator==(const NotSimpleViewIterBegin&, const NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible>&) {
+ return true;
+ }
+};
+
+template <bool CopyConvertible, bool MoveConvertible>
+struct NotSimpleViewConstIterBegin : InputIter<NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible>> {
+ constexpr NotSimpleViewConstIterBegin() = default;
+ constexpr NotSimpleViewConstIterBegin(NotSimpleViewConstIterBegin&&) = default;
+ constexpr NotSimpleViewConstIterBegin& operator=(const NotSimpleViewConstIterBegin&) = default;
+ constexpr NotSimpleViewConstIterBegin& operator=(NotSimpleViewConstIterBegin&&) = default;
+
+ constexpr NotSimpleViewConstIterBegin(const NotSimpleViewConstIterBegin&) {}
+ constexpr NotSimpleViewConstIterBegin(const NotSimpleViewIterBegin&)
+ requires CopyConvertible
+ {}
+ constexpr NotSimpleViewConstIterBegin(NotSimpleViewIterBegin&&)
+ requires MoveConvertible
+ {}
+
+ friend constexpr bool
+ operator==(const NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible>&, const NotSimpleViewIterEnd&) {
+ return true;
+ }
+ friend constexpr bool
+ operator==(const NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible>&, const NotSimpleViewIterBegin&) {
+ return true;
+ }
+};
+
+struct NotSimpleViewIterEnd : InputIter<NotSimpleViewIterEnd> {
+ template <bool CopyConvertible, bool MoveConvertible>
+ friend constexpr bool
+ operator==(const NotSimpleViewIterEnd&, const NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible>&) {
+ return true;
+ }
+ template <bool CopyConvertible, bool MoveConvertible>
+ friend constexpr bool
+ operator==(const NotSimpleViewIterEnd&, const NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible>&) {
+ return true;
+ }
+
+ friend constexpr bool operator==(const NotSimpleViewIterEnd&, const NotSimpleViewIterBegin&) { return true; }
+};
+
+template <bool CopyConvertible, bool MoveConvertible>
+struct NotSimpleViewConstIterEnd : InputIter<NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible>> {
+ constexpr NotSimpleViewConstIterEnd() = default;
+ constexpr NotSimpleViewConstIterEnd(NotSimpleViewConstIterEnd&&) = default;
+ constexpr NotSimpleViewConstIterEnd& operator=(const NotSimpleViewConstIterEnd&) = default;
+ constexpr NotSimpleViewConstIterEnd& operator=(NotSimpleViewConstIterEnd&&) = default;
+
+ constexpr NotSimpleViewConstIterEnd(const NotSimpleViewConstIterEnd&) {}
+ constexpr NotSimpleViewConstIterEnd(const NotSimpleViewIterEnd&)
+ requires CopyConvertible
+ {}
+ constexpr NotSimpleViewConstIterEnd(NotSimpleViewIterEnd&&)
+ requires MoveConvertible
+ {}
+
+ friend constexpr bool
+ operator==(const NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible>&, const NotSimpleViewIterEnd&) {
+ return true;
+ }
+ friend constexpr bool
+ operator==(const NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible>&, const NotSimpleViewIterBegin&) {
+ return true;
+ }
+};
+
+/*
+ * Goal: We will need a way to get a stride_view<true>::__iterator and a
+ * stride_view<false>::__iterator because those are the two possible types
+ * of the stride_view::__iterator constructor. The template value is determined
+ * by whether the stride_view::__iterator is derivative of a stride_view over a
+ * view that is simple.
+ *
+ * So, first things first, we need to build a stride_view over a (non-)simple view.
+ * There are (at least) two ways that a view can be non-simple:
+ * 1. The iterator type for const begin is different than the iterator type for begin
+ * 2. The iterator type for const end is different that the iterator type for end
+ *
+ * So, let's create two different classes where that is the case so that we can test
+ * for those conditions individually. We parameterize with a template to decide
+ * whether to
+ * 1. enable converting constructors between the non-const and the const version.
+ * That feature is important for testing the stride_view::__iterator<true> converting
+ * constructor from a stride_view::_iterator<false> iterator.
+ * 2. enable copyability. That feature is important for testing whether the requirement
+ * the that copy constructor for the stride_view::__iterator<false> type actually moves
+ * the underlying iterator.
+ */
+template <bool CopyConvertible = false, bool MoveConvertible = true>
+struct NotSimpleViewDifferentBegin : std::ranges::view_base {
+ constexpr NotSimpleViewConstIterBegin<CopyConvertible, MoveConvertible> begin() const { return {}; }
+ constexpr NotSimpleViewIterBegin begin() { return {}; }
+
+ constexpr NotSimpleViewIterEnd end() const { return {}; }
+};
+
+template <bool CopyConvertible = false, bool MoveConvertible = true>
+struct NotSimpleViewDifferentEnd : std::ranges::view_base {
+ constexpr NotSimpleViewIterBegin begin() const { return {}; }
+ constexpr NotSimpleViewConstIterEnd<CopyConvertible, MoveConvertible> end() const { return {}; }
+ constexpr NotSimpleViewIterEnd end() { return {}; }
+};
+
+constexpr bool non_simple_view_iter_ctor_test() {
+ using NotSimpleStrideView = std::ranges::stride_view<NotSimpleViewDifferentBegin<false>>;
+ using NotSimpleStrideViewIter = std::ranges::iterator_t<NotSimpleStrideView>;
+ using NotSimpleStrideViewIterConst = std::ranges::iterator_t<const NotSimpleStrideView>;
+ static_assert(!std::is_same_v<NotSimpleStrideViewIterConst, NotSimpleStrideViewIter>);
+ return true;
+}
+
+constexpr bool non_const_iterator_copy_ctor() {
+ // All tests share the following general configuration.
+ //
+ // Instantiate a stride view StrideView over a non-simple view (NotSimpleViewBeingStrided) whose
+ // 1. std::ranges::iterator_t<StrideView> base's type is NotSimpleViewBeingStridedIterator
+ // 2. std::ranges::iterator_t<const StrideView> base's type is NotSimpleViewBeingStridedConstIterator
+ // 3. NotSimpleViewBeingStridedIterator is ONLY move-convertible to NotSimpleViewBeingStridedConstIterator
+ // 4. std::ranges::sentinel_t are the same whether SV is const or not.
+ // 5. the type of StrideView::end is the same whether StrideView is const or not.
+ // 6. the type of StrideView::begin is stride_view::iterator<true> when StrideView is const and
+ // stride_view::iterator<false> when StrideView is non const.
+ // Visually, it looks like this:
+ //
+ // NotSimpleViewBeingStrided(Const)Iterator <-----
+ // ^ |
+ // | |
+ // | begin (const?) |
+ // | |
+ // NotSimpleViewBeingStrided |
+ // ^ |
+ // | |
+ // | Strides over |
+ // | |
+ // StrideView |
+ // | |
+ // | begin (const?) |
+ // | |
+ // \/ |
+ // StrideView(Const)Iter |
+ // | |
+ // | base |
+ // | |
+ // ---------------------------------
+
+ {
+ // Stride over non-simple view over whose iterators are copy convertible -- should look (statically)
+ // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
+ // a move of the current between the copied-from iterator to the copied-to iterator).
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<true, false>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are move convertible -- should look (statically)
+ // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
+ // a move of the current between the copied-from iterator to the copied-to iterator).
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, true>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are not convertible -- should not be able
+ // to copy construct the stride view's iterator.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, false>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(!std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(!std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are not convertible -- should not be able
+ // to copy construct the stride view's iterator.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, true>;
+ using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(std::convertible_to<NotSimpleViewBeingStridedIterator, NotSimpleViewBeingStridedIterator>);
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+
+ StrideView str{NotSimpleViewBeingStrided{}, 5};
+ // Confirm (5)
+ ASSERT_SAME_TYPE(StrideViewIter, decltype(str.begin()));
+
+ // Now, do what we wanted the whole time: make sure that we can copy construct a
+ // stride_view::iterator<true> from a stride_view::iterator<false>. The copy
+ // constructor requires that the new __current_ StrideViewConstIter (type
+ // NotSimpleViewBeingStridedConstIterator) be constructable
+ // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator).
+ StrideViewConstIter iterator_copy{str.begin()};
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are copy convertible -- should look (statically)
+ // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
+ // a move of the current between the copied-from iterator to the copied-to iterator).
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<true, false>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are move convertible -- should look (statically)
+ // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
+ // a move of the current between the copied-from iterator to the copied-to iterator).
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, true>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // Stride over non-simple view over whose iterators are not convertible -- should not be able
+ // to copy construct the stride view's iterator.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, false>;
+ // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ // using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ // using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ static_assert(!std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ static_assert(!std::constructible_from<StrideViewConstIter, StrideViewIter>);
+ }
+
+ {
+ // The NotSimpleViewBeingStrided template parameters mean that NotSimpleViewBeingStridedIterator
+ // can be move-converted to NotSimpleViewBeingStridedConstIterator but not copy-converted.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, true>;
+ using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
+ using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
+
+ using StrideView = std::ranges::stride_view<NotSimpleViewBeingStrided>;
+
+ using StrideViewIter = std::ranges::iterator_t<StrideView>;
+ using StrideViewConstIter = std::ranges::iterator_t<const StrideView>;
+
+ using StrideViewSentinel = std::ranges::sentinel_t<StrideView>;
+ using StrideViewConstSentinel = std::ranges::sentinel_t<const StrideView>;
+
+ // Confirm (1) and (2)
+ ASSERT_SAME_TYPE(NotSimpleViewBeingStridedIterator, decltype(std::declval<StrideViewIter>().base()));
+ ASSERT_SAME_TYPE(NotSimpleViewBeingStridedConstIterator, decltype(std::declval<StrideViewConstIter>().base()));
+ // Confirm (3)
+ static_assert(std::convertible_to<NotSimpleViewBeingStridedIterator, NotSimpleViewBeingStridedIterator>);
+ static_assert(std::convertible_to<StrideViewIter, StrideViewConstIter>);
+ // Confirm (4)
+ ASSERT_SAME_TYPE(StrideViewSentinel, StrideViewConstSentinel);
+
+ StrideView str{NotSimpleViewBeingStrided{}, 5};
+ // Confirm (5)
+ ASSERT_SAME_TYPE(StrideViewIter, decltype(str.begin()));
+
+ // Now, do what we wanted the whole time: make sure that we can copy construct a
+ // stride_view::iterator<true> from a stride_view::iterator<false>. The copy
+ // constructor requires that the new __current_ StrideViewConstIter (type
+ // NotSimpleViewBeingStridedConstIterator) be constructable
+ // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator).
+ StrideViewConstIter iterator_copy{str.begin()};
+ }
+ return true;
+}
+
+int main(int, char**) {
+ non_simple_view_iter_ctor_test();
+ static_assert(non_simple_view_iter_ctor_test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..9bf719e470d53
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// __iterator() requires default_initializable<iterator_t<_Base>> = default;
+
+#include <ranges>
+
+#include "../types.h"
+
+struct NonDefaultConstructibleIterator : InputIter<NonDefaultConstructibleIterator> {
+ NonDefaultConstructibleIterator() = delete;
+ constexpr NonDefaultConstructibleIterator(int) {}
+};
+
+struct ViewWithNonDefaultConstructibleIterator : std::ranges::view_base {
+ constexpr NonDefaultConstructibleIterator begin() const { return NonDefaultConstructibleIterator{5}; }
+ constexpr std::default_sentinel_t end() const { return {}; }
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<ViewWithNonDefaultConstructibleIterator> = true;
+
+// If the type of the iterator of the range being strided is non-default
+// constructible, then the stride view's iterator should not be default
+// constructible, either!
+static_assert(!std::is_default_constructible< std::ranges::iterator_t<ViewWithNonDefaultConstructibleIterator>>());
+// If the type of the iterator of the range being strided is default
+// constructible, then the stride view's iterator should be default
+// constructible, too!
+static_assert(std::is_default_constructible<
+ std::ranges::iterator_t< std::ranges::stride_view<std::ranges::ref_view<const int[3]>>>>());
+
+int main(int, char**) { return 0; }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..ceb700b5d1374
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator--()
+// constexpr __iterator operator--(int)
+
+#include <ranges>
+#include <vector>
+
+#include "../types.h"
+
+template <class T>
+concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
+template <class T>
+concept CanPreDecrement = std::is_same_v<T, decltype(--(std::declval<T>()))> && requires(T& t) { --t; };
+
+// What operators are valid for an iterator derived from a stride view
+// over an input view.
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(!std::ranges::bidirectional_range<InputView>);
+static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a forward view.
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(!std::ranges::bidirectional_range<ForwardView>);
+static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a bidirectional view.
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(std::ranges::bidirectional_range<BidirectionalView>);
+static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a random access view.
+using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
+
+static_assert(std::ranges::bidirectional_range<RandomAccessView>);
+static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
+
+template <typename Iter, typename Difference>
+ requires(std::bidirectional_iterator<Iter>)
+constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) {
+ using Base = BasicTestView<Iter, Iter>;
+
+ auto base_view_offset_zero = Base(begin, end);
+ // Because of the requires on the Iter template type, we are sure
+ // that the type of sv_incr_one is a bidirectional range.
+ auto sv_incr_diff = std::ranges::stride_view(base_view_offset_zero, delta);
+ auto sv_incr_end = sv_incr_diff.end();
+
+ // Recreate the "missing" calculation here -- to make sure that it matches.
+ auto missing = delta - (std::ranges::distance(base_view_offset_zero) % delta) % delta;
+
+ auto sought = end + (missing - delta);
+
+ assert(*sought == *(--sv_incr_end));
+ assert(*sought == *(sv_incr_end));
+
+ sv_incr_end = sv_incr_diff.end();
+ sv_incr_end--;
+ assert(*(end + (missing - delta)) == *(sv_incr_end));
+
+ return true;
+}
+
+int main(int, char**) {
+ constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_operator_decrement(vec.begin(), vec.end(), 3);
+ test_operator_decrement(arr, arr + 11, 3);
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
new file mode 100644
index 0000000000000..33faa77d1ff47
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
+// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class Iter>
+constexpr void testOne() {
+ using Range = BasicTestView<Iter, Iter>;
+ static_assert(std::ranges::common_range<Range>);
+ using StrideView = std::ranges::stride_view<Range>;
+
+ {
+ // simple test
+ {
+ int buffer[] = {0, 1, 2, -1, 4, 5, 6, 7};
+ const Range input(Iter{buffer}, Iter{buffer + 8});
+ const StrideView sv(input, 1);
+ const StrideView sv_too(input, 2);
+ auto b = sv.begin();
+ auto e = sv.end();
+ auto b_too = sv_too.begin();
+
+ assert(b == b);
+ assert(!(b != b));
+
+ // When Range is a bidirectional_range, the type of e is
+ // default_sentinel_t and those do not compare to one another.
+ if constexpr (!std::ranges::bidirectional_range<Range>) {
+ assert(e == e);
+ assert(!(e != e));
+ }
+ assert(!(b == e));
+ assert(b != e);
+
+ std::advance(b, 8);
+ std::advance(b_too, 4);
+
+ assert(b == b_too);
+ assert(!(b != b_too));
+
+ assert(b == b);
+ assert(!(b != b));
+
+ // See above.
+ if constexpr (!std::ranges::bidirectional_range<Range>) {
+ assert(e == e);
+ assert(!(e != e));
+ }
+
+ assert(b == e);
+ assert(!(b != e));
+ }
+
+ // Default-constructed iterators compare equal.
+ {
+ int buffer[] = {0, 1, 2, -1, 4, 5, 6};
+ const Range input(Iter{buffer}, Iter{buffer + 7});
+ const std::ranges::stride_view sv(input, 1);
+ using StrideViewIter = decltype(sv.begin());
+ StrideViewIter i1;
+ StrideViewIter i2;
+ assert(i1 == i2);
+ assert(!(i1 != i2));
+ }
+ }
+}
+
+constexpr bool test() {
+ testOne<forward_iterator<int*>>();
+ testOne<bidirectional_iterator<int*>>();
+ testOne<random_access_iterator<int*>>();
+ testOne<contiguous_iterator<int*>>();
+ testOne<int*>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
new file mode 100644
index 0000000000000..34404e056ef30
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
@@ -0,0 +1,426 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator++()
+// constexpr void operator++(int)
+// constexpr __iterator operator++(int)
+// constexpr __iterator& operator--()
+// constexpr __iterator operator--(int)
+// constexpr __iterator& operator+=(difference_type __n)
+// constexpr __iterator& operator-=(difference_type __n)
+// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
+// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
+
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanPlus = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
+template <class T>
+concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
+
+template <class T>
+concept CanMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
+ requires(T& t) { t - t; };
+
+template <class T>
+concept CanSentinelMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
+ std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
+ t - std::default_sentinel;
+ std::default_sentinel - t;
+ };
+
+template <class T>
+concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
+ t + 1;
+} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
+template <class T>
+concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
+
+template <class T>
+concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
+template <class T>
+concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
+
+template <class T>
+concept CanSubscript = requires(T& t) { t[5]; };
+
+// What operators are valid for an iterator derived from a stride view
+// over an input view.(sized sentinel)
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPlus<StrideViewOverInputViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
+static_assert(!CanMinus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a forward view.(sized sentinel)
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a bidirectional view. (sized sentinel)
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::less_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a random access view. (non sized sentinel)
+using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
+
+static_assert(
+ std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::less_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
+
+using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
+using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
+using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
+
+static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(!std::ranges::random_access_range<EqualableView>);
+static_assert(!std::three_way_comparable<EqualableView>);
+
+using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
+using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
+using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
+
+static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
+static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
+static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
+
+using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
+using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
+using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
+
+static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::ranges::random_access_range<UnEqualableView>);
+static_assert(!std::three_way_comparable<UnEqualableView>);
+
+template <typename Iter>
+ requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
+constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+ // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
+ // iterator and a default sentinel.
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+ static_assert(!std::ranges::forward_range<Base>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized input view.
+
+ static_assert(!CanPostDecrement<StrideViewIterator>);
+ static_assert(!CanPreDecrement<StrideViewIterator>);
+ static_assert(!CanPlus<StrideViewIterator>);
+ static_assert(!CanMinusEqual<StrideViewIterator>);
+ static_assert(!CanDifferencePlus<StrideViewIterator>);
+ static_assert(!CanDifferenceMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
+ static_assert(!CanSubscript<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(zero_begin, end);
+ auto base_view_offset_one = Base(one_begin, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
+ auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ auto sv_one_offset_oneth_index = sv_one_offset_begin;
+ auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
+ // Check positive __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
+ // Check negative __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
+
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ // assert((std::default_sentinel - )== 0);
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+
+ return true;
+}
+
+template <std::forward_iterator Iter, typename difference_type>
+constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) {
+ // Test the forward-range operator- between two iterators (i.e., no ceil) and
+ // an iterator and a default sentinel.
+ using Base = BasicTestView<Iter, Iter>;
+
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::ranges::forward_range<Base>);
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized forward view (even though it is actually much more than that!).
+
+ static_assert(CanMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(begin, end);
+ auto base_view_offset_one = Base(begin + 1, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
+
+ auto sv_one_offset_should_be_two = sv_one_offset_begin;
+ auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+ static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
+ assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
+
+ // Check positive __n with non-exact multiple of left's stride.
+ assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
+ assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
+
+ // Check negative __n with non-exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
+
+ // Make sure that all sentinel operations work!
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
+ return true;
+}
+
+constexpr bool test_properly_handling_missing() {
+ // Check whether __missing_ gets handled properly.
+ using Base = BasicTestView<int*, int*>;
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto base = Base(arr, arr + 10);
+ auto strider = std::ranges::stride_view<Base>(base, 7);
+
+ auto strider_iter = strider.end();
+
+ strider_iter--;
+ assert(*strider_iter == 8);
+
+ // Now that we are back among the valid, we should
+ // have a normal stride length back (i.e., __missing_
+ // should be equal to 0).
+ strider_iter--;
+ assert(*strider_iter == 1);
+
+ strider_iter++;
+ assert(*strider_iter == 8);
+
+ // By striding past the end, we are going to generate
+ // another __missing_ != 0 value.
+ strider_iter++;
+ assert(strider_iter == strider.end());
+
+ // Make sure that all sentinel operations work!
+ assert(strider.end() == std::default_sentinel);
+ assert(std::default_sentinel == strider.end());
+
+ assert(strider_iter - std::default_sentinel == 0);
+ assert(std::default_sentinel - strider.end() == 0);
+ assert(std::default_sentinel - strider_iter == 0);
+
+ // Let's make sure that the newly regenerated __missing__ gets used.
+ strider_iter += -2;
+ assert(*strider_iter == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ {
+ constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_operator_minus(arr, arr + 11, 4);
+ test_forward_operator_minus(vec.begin(), vec.end(), 4);
+ }
+
+ {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+ }
+
+ test_properly_handling_missing();
+ static_assert(test_properly_handling_missing());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..93f4a511a94ab
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
@@ -0,0 +1,205 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator++()
+// constexpr void operator++(int)
+// constexpr __iterator operator++(int)
+
+#include <iterator>
+#include <vector>
+
+#include "../types.h"
+
+template <class T>
+concept CanPostIncrementVoid = std::is_same_v<void, decltype(std::declval<T>()++)> && requires(T& t) { t++; };
+template <class T>
+concept CanPostIncrementIterator = std::is_same_v<T, decltype(std::declval<T>()++)> && requires(T& t) { t = t++; };
+template <class T>
+concept CanPreIncrementIterator = std::is_same_v<T&, decltype(++(std::declval<T>()))> && requires(T& t) { t = ++t; };
+
+// A stride view with a base that is a non forward range returns void from operator++
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+static_assert(CanPostIncrementVoid<StrideViewOverInputViewIterator>);
+static_assert(!CanPostIncrementIterator<StrideViewOverInputViewIterator>);
+static_assert(CanPreIncrementIterator<StrideViewOverInputViewIterator>);
+
+// A stride view with a base that is a forward range returns void from operator++
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+static_assert(!CanPostIncrementVoid<StrideViewOverForwardViewIterator>);
+static_assert(CanPostIncrementIterator<StrideViewOverForwardViewIterator>);
+static_assert(CanPreIncrementIterator<StrideViewOverForwardViewIterator>);
+
+template <typename Iter>
+ requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
+constexpr bool test_non_forward_operator_increment(Iter zero_begin, Iter three_begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+
+ auto base_view_offset_zero = Base(zero_begin, end);
+ auto base_view_offset_three = Base(three_begin, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_three_offset = std::ranges::stride_view(base_view_offset_three, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_three_offset_begin = stride_view_over_base_three_offset.begin();
+
+ auto sv_zero_offset_third_index = sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ ++sv_zero_offset_third_index;
+ assert(*sv_three_offset_begin == *sv_zero_offset_third_index);
+
+ sv_zero_offset_third_index = sv_zero_offset_begin;
+ sv_zero_offset_third_index++;
+ assert(*sv_three_offset_begin == *sv_zero_offset_third_index);
+
+ // See if both get to the end (with pre-increment).
+ auto sv_zero_offset_incremented_to_end = sv_zero_offset_begin;
+ ++sv_zero_offset_incremented_to_end; // 3
+ ++sv_zero_offset_incremented_to_end; // 6
+ ++sv_zero_offset_incremented_to_end; // 9
+ ++sv_zero_offset_incremented_to_end; // End
+
+ auto sv_three_offset_incremented_to_end = sv_three_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ ++sv_three_offset_incremented_to_end; // 6
+ ++sv_three_offset_incremented_to_end; // 9
+ ++sv_three_offset_incremented_to_end; // End
+
+ assert(sv_three_offset_incremented_to_end == sv_zero_offset_incremented_to_end);
+ assert(sv_three_offset_incremented_to_end == stride_view_over_base_three_offset.end());
+ assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end());
+
+ // See if both get to the end (with post-increment).
+ sv_zero_offset_incremented_to_end = sv_zero_offset_begin;
+ sv_zero_offset_incremented_to_end++; // 3
+ sv_zero_offset_incremented_to_end++; // 6
+ sv_zero_offset_incremented_to_end++; // 9
+ sv_zero_offset_incremented_to_end++; // End
+
+ sv_three_offset_incremented_to_end = sv_three_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ sv_three_offset_incremented_to_end++; // 6
+ sv_three_offset_incremented_to_end++; // 9
+ sv_three_offset_incremented_to_end++; // End
+
+ assert(sv_three_offset_incremented_to_end == sv_zero_offset_incremented_to_end);
+ assert(sv_three_offset_incremented_to_end == stride_view_over_base_three_offset.end());
+ assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end());
+
+ return true;
+}
+
+template <std::forward_iterator Iter>
+constexpr bool test_forward_operator_increment(Iter begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::ranges::forward_range<Base>);
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(begin, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+
+ // Create a ground truth for comparison.
+ auto sv_zero_offset_third_index_key = stride_view_over_base_zero_offset.begin();
+ sv_zero_offset_third_index_key++;
+
+ auto sv_zero_offset_third_index = ++sv_zero_offset_begin;
+ assert(*sv_zero_offset_third_index == *sv_zero_offset_begin);
+ assert(*sv_zero_offset_third_index == *sv_zero_offset_third_index_key);
+
+ sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ sv_zero_offset_third_index = sv_zero_offset_begin;
+ sv_zero_offset_third_index++;
+ assert(*sv_zero_offset_third_index == *sv_zero_offset_third_index_key);
+
+ sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_zero_offset_incremented_to_end = sv_zero_offset_begin;
+ ++sv_zero_offset_incremented_to_end; // 3
+ ++sv_zero_offset_incremented_to_end; // 6
+ ++sv_zero_offset_incremented_to_end; // 9
+ ++sv_zero_offset_incremented_to_end; // End
+
+ auto sv_zero_offset_incremented_to_end_reset = sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 3
+ sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 6
+ sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 9
+ sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // End
+
+ assert(sv_zero_offset_incremented_to_end == sv_zero_offset_incremented_to_end_reset);
+ assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end());
+
+ sv_zero_offset_incremented_to_end = sv_zero_offset_begin;
+ sv_zero_offset_incremented_to_end++; // 3
+ sv_zero_offset_incremented_to_end++; // 6
+ sv_zero_offset_incremented_to_end++; // 9
+ sv_zero_offset_incremented_to_end++; // End
+ assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end());
+
+ return true;
+}
+
+constexpr bool test_properly_handling_missing() {
+ // Check whether __missing_ gets handled properly.
+ using Base = BasicTestView<int*, int*>;
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto base = Base(arr, arr + 10);
+ auto strider = std::ranges::stride_view<Base>(base, 7);
+
+ auto strider_iter = strider.end();
+
+ strider_iter--;
+ assert(*strider_iter == 8);
+
+ // Now that we are back among the valid, we should
+ // have a normal stride length back (i.e., __missing_
+ // should be equal to 0).
+ strider_iter--;
+ assert(*strider_iter == 1);
+
+ strider_iter++;
+ assert(*strider_iter == 8);
+
+ // By striding past the end, we are going to generate
+ // another __missing_ != 0 value.
+ strider_iter++;
+ assert(strider_iter == strider.end());
+
+ // Make sure that all sentinel operations work!
+ assert(strider.end() == std::default_sentinel);
+ assert(std::default_sentinel == strider.end());
+
+ assert(strider_iter - std::default_sentinel == 0);
+ assert(std::default_sentinel - strider.end() == 0);
+ assert(std::default_sentinel - strider_iter == 0);
+
+ // Let's make sure that the newly regenerated __missing__ gets used.
+ strider_iter += -2;
+ assert(*strider_iter == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ {
+ constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_operator_increment(arr, arr + 11);
+ test_forward_operator_increment(vec.begin(), vec.end());
+ }
+
+ {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ test_non_forward_operator_increment(SizedInputIter(arr), SizedInputIter(arr + 3), SizedInputIter(arr + 10));
+ }
+
+ test_properly_handling_missing();
+ static_assert(test_properly_handling_missing());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..3f355b5217dfa
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// friend constexpr range_rvalue_reference_t<_Base> iter_move(__iterator const& __it)
+// noexcept(noexcept(ranges::iter_move(__it.__current_)))
+
+#include <ranges>
+#include <vector>
+
+#include "../types.h"
+#include "test_macros.h"
+
+template <typename T>
+concept iter_moveable = requires(T&& t) { std::ranges::iter_move(t); };
+
+constexpr bool test() {
+ {
+ int a[] = {4, 3, 2, 1};
+
+ int iter_move_counter(0);
+ using View = IterMoveIterSwapTestRange<int*, true, true>;
+ using StrideView = std::ranges::stride_view<View>;
+ auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
+
+ static_assert(iter_moveable<std::ranges::iterator_t<StrideView>>);
+ ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb)));
+ static_assert(noexcept(std::ranges::iter_move(svb)));
+
+ auto&& result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 1);
+ assert(result == 4);
+
+ svb++;
+ result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 2);
+ assert(result == 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+
+ int iter_move_counter(0);
+ using View = IterMoveIterSwapTestRange<int*, true, false>;
+ using StrideView = std::ranges::stride_view<View>;
+ auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
+
+ static_assert(iter_moveable<std::ranges::iterator_t<StrideView>>);
+ ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb)));
+ static_assert(!noexcept(std::ranges::iter_move(svb)));
+
+ auto&& result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 1);
+ assert(result == 1);
+
+ svb++;
+ result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 2);
+ assert(result == 2);
+ }
+
+ {
+ std::vector<int> a = {4, 5, 6, 7, 8};
+
+ int iter_move_counter(0);
+ using View = IterMoveIterSwapTestRange<std::vector<int>::iterator, true, false>;
+
+ using StrideView = std::ranges::stride_view<View>;
+ auto svb = StrideView(View(a.begin(), a.end(), &iter_move_counter), 1).begin();
+
+ static_assert(!noexcept(std::ranges::iter_move(svb)));
+
+ auto&& result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 1);
+ assert(result == 4);
+
+ svb++;
+ result = std::ranges::iter_move(svb);
+ assert(iter_move_counter == 2);
+ assert(result == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..75eccbfedac86
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// friend constexpr void iter_swap(__iterator const& __x, __iterator const& __y)
+// noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+// requires indirectly_swappable<iterator_t<_Base>>
+
+#include <ranges>
+
+#include "../types.h"
+
+template <typename T>
+concept swappable = requires(T&& t, T&& u) { std::ranges::iter_swap(t, u); };
+
+constexpr bool test() {
+ {
+ int a[] = {1, 2, 3, 4};
+ int b[] = {5, 6, 7, 8};
+
+ int iter_move_counter_one(0);
+ int iter_move_counter_two(0);
+ using View = IterMoveIterSwapTestRange<int*, true, true>;
+ using StrideView = std::ranges::stride_view<View>;
+ auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin();
+ auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin();
+
+ static_assert(swappable<std::ranges::iterator_t<StrideView>>);
+ static_assert(noexcept(std::ranges::iter_swap(svba, svbb)));
+
+ assert(a[0] == 1);
+ assert(b[0] == 5);
+
+ std::ranges::iter_swap(svba, svbb);
+ assert(iter_move_counter_one == 1);
+ assert(iter_move_counter_two == 1);
+
+ assert(a[0] == 5);
+ assert(b[0] == 1);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ int b[] = {5, 6, 7, 8};
+
+ int iter_move_counter_one(0);
+ int iter_move_counter_two(0);
+ using View = IterMoveIterSwapTestRange<int*, true, false>;
+ using StrideView = std::ranges::stride_view<View>;
+ auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin();
+ auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin();
+
+ static_assert(swappable<std::ranges::iterator_t<StrideView>>);
+ static_assert(!noexcept(std::ranges::iter_swap(svba, svbb)));
+
+ assert(a[0] == 1);
+ assert(b[0] == 5);
+
+ std::ranges::iter_swap(svba, svbb);
+
+ assert(iter_move_counter_one == 1);
+ assert(iter_move_counter_two == 1);
+ assert(a[0] == 5);
+ assert(b[0] == 1);
+ }
+
+ {
+ using View = IterMoveIterSwapTestRange<int*, false, false>;
+ using StrideView = std::ranges::stride_view<View>;
+
+ static_assert(!swappable<std::ranges::iterator_t<StrideView>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
new file mode 100644
index 0000000000000..5a35ecc066c43
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
@@ -0,0 +1,426 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator++()
+// constexpr void operator++(int)
+// constexpr __iterator operator++(int)
+// constexpr __iterator& operator--()
+// constexpr __iterator operator--(int)
+// constexpr __iterator& operator+=(difference_type __n)
+// constexpr __iterator& operator-=(difference_type __n)
+// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
+// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
+
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanPlusEqual = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
+template <class T>
+concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
+
+template <class T>
+concept CanMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
+ requires(T& t) { t - t; };
+
+template <class T>
+concept CanSentinelMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
+ std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
+ t - std::default_sentinel;
+ std::default_sentinel - t;
+ };
+
+template <class T>
+concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
+ t + 1;
+} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
+template <class T>
+concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
+
+template <class T>
+concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
+template <class T>
+concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
+
+template <class T>
+concept CanSubscript = requires(T& t) { t[5]; };
+
+// What operators are valid for an iterator derived from a stride view
+// over an input view.(sized sentinel)
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPlusEqual<StrideViewOverInputViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
+static_assert(!CanMinus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a forward view.(sized sentinel)
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPlusEqual<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a bidirectional view. (sized sentinel)
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanPlusEqual<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::less_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a random access view. (non sized sentinel)
+using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPlusEqual<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
+
+static_assert(
+ std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::less_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
+
+using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
+using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
+using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
+
+static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(!std::ranges::random_access_range<EqualableView>);
+static_assert(!std::three_way_comparable<EqualableView>);
+
+using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
+using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
+using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
+
+static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
+static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
+static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
+
+using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
+using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
+using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
+
+static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::ranges::random_access_range<UnEqualableView>);
+static_assert(!std::three_way_comparable<UnEqualableView>);
+
+template <typename Iter>
+ requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
+constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+ // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
+ // iterator and a default sentinel.
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+ static_assert(!std::ranges::forward_range<Base>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized input view.
+
+ static_assert(!CanPostDecrement<StrideViewIterator>);
+ static_assert(!CanPreDecrement<StrideViewIterator>);
+ static_assert(!CanPlusEqual<StrideViewIterator>);
+ static_assert(!CanMinusEqual<StrideViewIterator>);
+ static_assert(!CanDifferencePlus<StrideViewIterator>);
+ static_assert(!CanDifferenceMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
+ static_assert(!CanSubscript<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(zero_begin, end);
+ auto base_view_offset_one = Base(one_begin, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
+ auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ auto sv_one_offset_oneth_index = sv_one_offset_begin;
+ auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
+ // Check positive __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
+ // Check negative __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
+
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ // assert((std::default_sentinel - )== 0);
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+
+ return true;
+}
+
+template <std::forward_iterator Iter, typename difference_type>
+constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) {
+ // Test the forward-range operator- between two iterators (i.e., no ceil) and
+ // an iterator and a default sentinel.
+ using Base = BasicTestView<Iter, Iter>;
+
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::ranges::forward_range<Base>);
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized forward view (even though it is actually much more than that!).
+
+ static_assert(CanMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(begin, end);
+ auto base_view_offset_one = Base(begin + 1, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
+
+ auto sv_one_offset_should_be_two = sv_one_offset_begin;
+ auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+ static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
+ assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
+
+ // Check positive __n with non-exact multiple of left's stride.
+ assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
+ assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
+
+ // Check negative __n with non-exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
+
+ // Make sure that all sentinel operations work!
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
+ return true;
+}
+
+constexpr bool test_properly_handling_missing() {
+ // Check whether __missing_ gets handled properly.
+ using Base = BasicTestView<int*, int*>;
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto base = Base(arr, arr + 10);
+ auto strider = std::ranges::stride_view<Base>(base, 7);
+
+ auto strider_iter = strider.end();
+
+ strider_iter--;
+ assert(*strider_iter == 8);
+
+ // Now that we are back among the valid, we should
+ // have a normal stride length back (i.e., __missing_
+ // should be equal to 0).
+ strider_iter--;
+ assert(*strider_iter == 1);
+
+ strider_iter++;
+ assert(*strider_iter == 8);
+
+ // By striding past the end, we are going to generate
+ // another __missing_ != 0 value.
+ strider_iter++;
+ assert(strider_iter == strider.end());
+
+ // Make sure that all sentinel operations work!
+ assert(strider.end() == std::default_sentinel);
+ assert(std::default_sentinel == strider.end());
+
+ assert(strider_iter - std::default_sentinel == 0);
+ assert(std::default_sentinel - strider.end() == 0);
+ assert(std::default_sentinel - strider_iter == 0);
+
+ // Let's make sure that the newly regenerated __missing__ gets used.
+ strider_iter += -2;
+ assert(*strider_iter == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ {
+ constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_operator_minus(arr, arr + 11, 4);
+ test_forward_operator_minus(vec.begin(), vec.end(), 4);
+ }
+
+ {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+ }
+
+ test_properly_handling_missing();
+ static_assert(test_properly_handling_missing());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
new file mode 100644
index 0000000000000..f8b077770df51
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
@@ -0,0 +1,426 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator++()
+// constexpr void operator++(int)
+// constexpr __iterator operator++(int)
+// constexpr __iterator& operator--()
+// constexpr __iterator operator--(int)
+// constexpr __iterator& operator+=(difference_type __n)
+// constexpr __iterator& operator-=(difference_type __n)
+// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
+// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
+
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanPlus = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
+template <class T>
+concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
+
+template <class T>
+concept CanMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
+ requires(T& t) { t - t; };
+
+template <class T>
+concept CanSentinelMinus =
+ // Note: Do *not* use std::iterator_traits here because T may not have
+ // all the required pieces when it is not a forward_range.
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
+ std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
+ t - std::default_sentinel;
+ std::default_sentinel - t;
+ };
+
+template <class T>
+concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
+ t + 1;
+} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
+template <class T>
+concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
+
+template <class T>
+concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
+template <class T>
+concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
+
+template <class T>
+concept CanSubscript = requires(T& t) { t[5]; };
+
+// What operators are valid for an iterator derived from a stride view
+// over an input view.(sized sentinel)
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
+static_assert(!CanPlus<StrideViewOverInputViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
+static_assert(!CanMinus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a forward view.(sized sentinel)
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
+
+static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
+static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
+static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+static_assert(
+ !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a bidirectional view. (sized sentinel)
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
+static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!std::is_invocable_v<std::less<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::less_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+static_assert(!std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverBidirectionalViewIterator,
+ StrideViewOverBidirectionalViewIterator>);
+
+static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
+
+// What operators are valid for an iterator derived from a stride view
+// over a random access view. (non sized sentinel)
+using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
+
+static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
+static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
+static_assert(
+ std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
+static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
+
+static_assert(
+ std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::less_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+static_assert(std::is_invocable_v<std::greater_equal<>,
+ StrideViewOverRandomAccessViewIterator,
+ StrideViewOverRandomAccessViewIterator>);
+
+static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
+
+using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
+using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
+using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
+
+static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(!std::ranges::random_access_range<EqualableView>);
+static_assert(!std::three_way_comparable<EqualableView>);
+
+using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
+using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
+using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
+
+static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
+static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
+static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
+
+using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
+using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
+using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
+
+static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
+
+static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::ranges::random_access_range<UnEqualableView>);
+static_assert(!std::three_way_comparable<UnEqualableView>);
+
+template <typename Iter>
+ requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
+constexpr bool test_non_forward_operator_plus(Iter zero_begin, Iter one_begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+ // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
+ // iterator and a default sentinel.
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+ static_assert(!std::ranges::forward_range<Base>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized input view.
+
+ static_assert(!CanPostDecrement<StrideViewIterator>);
+ static_assert(!CanPreDecrement<StrideViewIterator>);
+ static_assert(!CanPlus<StrideViewIterator>);
+ static_assert(!CanMinusEqual<StrideViewIterator>);
+ static_assert(!CanDifferencePlus<StrideViewIterator>);
+ static_assert(!CanDifferenceMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
+ static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
+ static_assert(!CanSubscript<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(zero_begin, end);
+ auto base_view_offset_one = Base(one_begin, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
+ auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+ auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ auto sv_one_offset_oneth_index = sv_one_offset_begin;
+ auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
+ // Check positive __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
+ assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
+ // Check negative __n with non-exact multiple of left's stride (will do ceil here).
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
+ assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
+
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ // assert((std::default_sentinel - )== 0);
+
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+
+ return true;
+}
+
+template <std::forward_iterator Iter, typename difference_type>
+constexpr bool test_forward_operator_plus(Iter begin, Iter end, difference_type distance) {
+ // Test the forward-range operator- between two iterators (i.e., no ceil) and
+ // an iterator and a default sentinel.
+ using Base = BasicTestView<Iter, Iter>;
+
+ using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
+ static_assert(std::ranges::forward_range<Base>);
+ static_assert(std::weakly_incrementable<StrideViewIterator>);
+
+ // First, what operators are valid for an iterator derived from a stride view
+ // over a sized forward view (even though it is actually much more than that!).
+
+ static_assert(CanMinus<StrideViewIterator>);
+ static_assert(CanSentinelMinus<StrideViewIterator>);
+
+ auto base_view_offset_zero = Base(begin, end);
+ auto base_view_offset_one = Base(begin + 1, end);
+ auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
+ auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
+
+ auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
+ auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+
+ auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
+ auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
+
+ auto sv_one_offset_should_be_two = sv_one_offset_begin;
+ auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
+
+ static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
+ static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
+ static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
+
+ // Check positive __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
+ assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
+
+ // Check positive __n with non-exact multiple of left's stride.
+ assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
+ assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
+
+ // Check negative __n with exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
+ assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
+
+ // Check negative __n with non-exact multiple of left's stride.
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
+ assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
+
+ // Make sure that all sentinel operations work!
+ assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
+ assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
+
+ assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
+ std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
+ -std::ranges::distance(stride_view_over_base_zero_offset));
+ assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
+ assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
+ return true;
+}
+
+constexpr bool test_properly_handling_missing() {
+ // Check whether __missing_ gets handled properly.
+ using Base = BasicTestView<int*, int*>;
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto base = Base(arr, arr + 10);
+ auto strider = std::ranges::stride_view<Base>(base, 7);
+
+ auto strider_iter = strider.end();
+
+ strider_iter--;
+ assert(*strider_iter == 8);
+
+ // Now that we are back among the valid, we should
+ // have a normal stride length back (i.e., __missing_
+ // should be equal to 0).
+ strider_iter--;
+ assert(*strider_iter == 1);
+
+ strider_iter++;
+ assert(*strider_iter == 8);
+
+ // By striding past the end, we are going to generate
+ // another __missing_ != 0 value.
+ strider_iter++;
+ assert(strider_iter == strider.end());
+
+ // Make sure that all sentinel operations work!
+ assert(strider.end() == std::default_sentinel);
+ assert(std::default_sentinel == strider.end());
+
+ assert(strider_iter - std::default_sentinel == 0);
+ assert(std::default_sentinel - strider.end() == 0);
+ assert(std::default_sentinel - strider_iter == 0);
+
+ // Let's make sure that the newly regenerated __missing__ gets used.
+ strider_iter += -2;
+ assert(*strider_iter == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ {
+ constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_operator_plus(arr, arr + 11, 4);
+ test_forward_operator_plus(vec.begin(), vec.end(), 4);
+ }
+
+ {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ test_non_forward_operator_plus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+ }
+
+ test_properly_handling_missing();
+ static_assert(test_properly_handling_missing());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
new file mode 100644
index 0000000000000..59d5473fa3215
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator operator+(difference_type __n, const __iterator &__i)
+// constexpr __iterator operator+(const __iterator &__i, difference_type __n)
+
+#include <ranges>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanPlus =
+ std::is_same_v<T, decltype(std::declval<T>() + std::declval<typename T::__iterator::difference_type>())> &&
+ std::is_same_v<T, decltype(std::declval<typename T::__iterator::difference_type>() + std::declval<T>())> &&
+ requires(T& t, T::__iterator::difference_type& u) { t = t + u; } &&
+ requires(T& t, T::__iterator::difference_type& u) { t = u + t; };
+
+// Make sure that we cannot use + on a stride view iterator and difference_type
+// over an input view.(sized sentinel)
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(std::ranges::input_range<InputView>);
+static_assert(!CanPlus<StrideViewOverInputViewIterator>);
+
+// Make sure that we cannot use + on a stride view iterator and difference_type
+// over a forward view.(sized sentinel)
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(std::ranges::forward_range<ForwardView>);
+static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
+
+// Make sure that we cannot use + on a stride view iterator and difference_type
+// over a bidirectional view. (sized sentinel)
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(std::ranges::bidirectional_range<BidirectionalView>);
+static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
+
+// Make sure that we can use += on a stride view iterator and difference_type
+// over a random access view. (non sized sentinel)
+template <typename RandomAccessIterator = random_access_iterator<int*>>
+using RandomAccessView = BasicTestView<RandomAccessIterator>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView<>>>;
+
+static_assert(std::ranges::random_access_range<RandomAccessView<>>);
+static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
+
+constexpr bool test_random_access_operator_plus_equal() {
+ using Iter = std::vector<int>::iterator;
+ using Diff = Iter::difference_type;
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ // Test the operator+ between an iterator and its difference type. Pay attention solely to
+ // stride views over random-access ranges because operator+ is not applicable to others.
+
+ auto begin = vec.begin();
+ auto end = vec.end();
+ Diff distance = 4;
+
+ // Do not use the RandomAccessView defined in types.h to give the test user more power
+ // to customize an iterator and a default sentinel.
+ using Base = RandomAccessView<Iter>;
+ static_assert(std::ranges::random_access_range<Base>);
+
+ auto base_view = Base(begin, end);
+ auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
+
+ auto base_view_offset = Base(begin + distance, end);
+ auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1);
+
+ assert(*(stride_view_over_base_view.begin() + distance) == *(stride_view_over_base_view_offset.begin()));
+
+ auto distance_to_last = (end - 1) - begin;
+ auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, distance_to_last);
+
+ assert(*(stride_view_over_base_view_big_step.begin() + 1) ==
+ *(stride_view_over_base_view.begin() + distance_to_last));
+ assert((stride_view_over_base_view_big_step.begin() + 2) == (stride_view_over_base_view.end()));
+
+ return true;
+}
+
+consteval bool do_static_tests() {
+ assert(test_random_access_operator_plus_equal());
+ return true;
+}
+
+int main(int, char**) {
+ static_assert(do_static_tests());
+ assert(do_static_tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
new file mode 100644
index 0000000000000..01582e22066c6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr __iterator& operator+=(difference_type __n)
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanPlus =
+ std::is_same_v<T&, decltype(std::declval<T>() += std::declval<typename T::__iterator::difference_type>())> &&
+ requires(T& t, T::__iterator::difference_type& u) { t += u; };
+
+// Make sure that we cannot use += on a stride view iterator
+// over an input view.(sized sentinel)
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+
+static_assert(std::ranges::input_range<InputView>);
+static_assert(!CanPlus<StrideViewOverInputViewIterator>);
+
+// Make sure that we cannot use += on a stride view iterator
+// over a forward view.(sized sentinel)
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+
+static_assert(std::ranges::forward_range<ForwardView>);
+static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
+
+// Make sure that we cannot use += on a stride view iterator
+// over a bidirectional view. (sized sentinel)
+using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
+
+static_assert(std::ranges::bidirectional_range<BidirectionalView>);
+static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
+
+// Make sure that we can use += on a stride view iterator
+// over a random access view. (non sized sentinel)
+template <typename RandomAccessIterator = random_access_iterator<int*>>
+using RandomAccessView = BasicTestView<RandomAccessIterator>;
+using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView<>>>;
+
+static_assert(std::ranges::random_access_range<RandomAccessView<>>);
+static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
+
+constexpr bool test_random_access_operator_plus_equal() {
+ using Iter = std::vector<int>::iterator;
+ using Diff = Iter::difference_type;
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ // Test the operator+ between an iterator and its difference type. Pay attention solely to
+ // stride views over random-access ranges because operator+ is not applicable to others.
+
+ auto begin = vec.begin();
+ auto end = vec.end();
+ Diff distance = 4;
+
+ // Test the forward-range operator+= between an iterator and its difference type.
+ // Do not use the RandomAccessView defined in types.h to give the test user more power
+ // to customize an iterator and a default sentinel.
+ using Base = RandomAccessView<Iter>;
+ static_assert(std::ranges::random_access_range<Base>);
+
+ auto base_view = Base(begin, end);
+ auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
+
+ auto base_view_offset = Base(begin + distance, end);
+ auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1);
+
+ auto sv_bv_begin = stride_view_over_base_view.begin();
+ auto sv_bv_offset_begin = stride_view_over_base_view_offset.begin();
+
+ auto sv_bv_begin_after_distance = sv_bv_begin += distance;
+ assert(*sv_bv_begin == *sv_bv_offset_begin);
+ assert(*sv_bv_begin_after_distance == *sv_bv_offset_begin);
+
+ auto big_step = (end - 1) - begin;
+ auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, big_step);
+ sv_bv_begin = stride_view_over_base_view_big_step.begin();
+
+ // This += should move us into a position where the __missing_ will come into play.
+ // Do a -= 1 here to confirm that the __missing_ is taken into account.
+ sv_bv_begin += 2;
+ sv_bv_begin -= 1;
+ assert(*sv_bv_begin == *(stride_view_over_base_view.begin() + big_step));
+ return true;
+}
+
+consteval bool do_static_tests() {
+ assert(test_random_access_operator_plus_equal());
+ return true;
+}
+
+int main(int, char**) {
+ static_assert(do_static_tests());
+ assert(do_static_tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
new file mode 100644
index 0000000000000..4a90ee750bf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// constexpr auto size()
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+// There is no size member function on a stride view over a view that
+// is *not* a sized range
+static_assert(!std::ranges::sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(!std::ranges::sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a size member function on a stride view over a view that
+// *is* a sized range
+static_assert(std::ranges::sized_range<BasicTestView<int*, sentinel_wrapper<int*>, true>>);
+static_assert(std::ranges::sized_range<std::ranges::stride_view<BasicTestView<int*, sentinel_wrapper<int*>, true>>>);
+
+constexpr bool test() {
+ {
+ // Test with stride as exact multiple of number of elements in view strided over.
+ constexpr auto iota_twelve = std::views::iota(0, 12);
+ static_assert(std::ranges::sized_range<decltype(iota_twelve)>);
+ constexpr auto stride_iota_twelve = std::views::stride(iota_twelve, 3);
+ static_assert(std::ranges::sized_range<decltype(stride_iota_twelve)>);
+ static_assert(4 == stride_iota_twelve.size(), "Striding by 3 through a 12 member list has size 4.");
+ }
+
+ {
+ // Test with stride as inexact multiple of number of elements in view strided over.
+ constexpr auto iota_twenty_two = std::views::iota(0, 22);
+ static_assert(std::ranges::sized_range<decltype(iota_twenty_two)>);
+ constexpr auto stride_iota_twenty_two = std::views::stride(iota_twenty_two, 3);
+ static_assert(std::ranges::sized_range<decltype(stride_iota_twenty_two)>);
+ static_assert(8 == stride_iota_twenty_two.size(), "Striding by 3 through a 22 member list has size 8.");
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
new file mode 100644
index 0000000000000..822f4f360f263
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// constexpr range_difference_t<_View> stride() const noexcept;
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+constexpr bool test() {
+ using View = BasicTestView<cpp17_input_iterator<int*>>;
+ int arr[]{1, 2, 3};
+ auto arrv(View(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3)));
+ // Mark str const so that we confirm that stride is a const member function.
+ const std::ranges::stride_view<View> str(arrv, 1);
+
+ // Make sure that stride member function is noexcept.
+ static_assert(noexcept(str.stride()));
+ // Make sure that the type returned by stride matches what is expected.
+ ASSERT_SAME_TYPE(std::ranges::range_difference_t<View>, decltype(str.stride()));
+ // Make sure that we get back a stride equal to the one that we gave in the ctor.
+ assert(str.stride() == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
new file mode 100644
index 0000000000000..5879c5022006e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -0,0 +1,296 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
+
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+// Concepts
+
+template <typename Iter>
+concept IterDifferable = std::invocable<std::minus<>, Iter, Iter>;
+
+// Iterators
+
+// The base for an iterator that keeps a count of the times that it is
+// moved and copied.
+template <class Derived, std::input_iterator Iter>
+struct IterBase {
+ using value_type = typename std::iterator_traits<Iter>::value_type;
+ using difference_type = typename std::iterator_traits<Iter>::difference_type;
+
+ int* move_counter = nullptr;
+ int* copy_counter = nullptr;
+
+ Iter value_{};
+
+ constexpr IterBase() = default;
+ constexpr explicit IterBase(Iter value) : value_(value) {}
+
+ constexpr IterBase(const IterBase& other) noexcept {
+ copy_counter = other.copy_counter;
+ move_counter = other.move_counter;
+ if (copy_counter != nullptr) {
+ (*copy_counter)++;
+ }
+ value_ = other.value_;
+ }
+
+ constexpr IterBase(IterBase&& other) noexcept {
+ copy_counter = other.copy_counter;
+ move_counter = other.move_counter;
+ if (move_counter != nullptr) {
+ (*move_counter)++;
+ }
+ value_ = std::move(other.value_);
+ }
+ constexpr IterBase& operator=(const IterBase& other) = default;
+ constexpr IterBase& operator=(IterBase&& other) = default;
+};
+
+// The base for an input iterator that keeps a count of the times that it is
+// moved and copied.
+template <class Derived, std::input_iterator Iter = int*, bool IsSized = false>
+ requires((!IsSized) || (IsSized && IterDifferable<Iter>))
+struct InputIter : IterBase<Derived, Iter> {
+ using Base = IterBase<Derived, Iter>;
+
+ using typename Base::difference_type;
+ using typename Base::value_type;
+
+ using iterator_concept = std::input_iterator_tag;
+ using iterator_category = std::input_iterator_tag;
+
+ using Base::Base;
+
+ constexpr value_type operator*() const { return *Base::value_; }
+ constexpr Derived& operator++() {
+ Base::value_++;
+ return static_cast<Derived&>(*this);
+ }
+ constexpr Derived operator++(int) {
+ auto nv = *this;
+ Base::value_++;
+ return nv;
+ }
+ friend constexpr bool operator==(const Derived& left, const Derived& right) { return left.value_ == right.value_; }
+ friend constexpr difference_type operator-(const Derived& left, const Derived& right)
+ requires IsSized
+ {
+ return left.value_ - right.value_;
+ }
+};
+
+// In input iterator that is unsized.
+struct UnsizedInputIter : InputIter<UnsizedInputIter, int*, false> {
+ using InputIter::InputIter;
+};
+static_assert(std::input_iterator<UnsizedInputIter>);
+static_assert(!std::sized_sentinel_for<UnsizedInputIter, UnsizedInputIter>);
+
+// In input iterator that is sized.
+struct SizedInputIter : InputIter<SizedInputIter, int*, true> {
+ using InputIter::InputIter;
+};
+static_assert(std::input_iterator<SizedInputIter>);
+static_assert(std::sized_sentinel_for<SizedInputIter, SizedInputIter>);
+
+// Views
+
+// Put IterMoveIterSwapTestRangeIterator in a namespace to test ADL of CPOs iter_swap and iter_move
+// (see iter_swap.pass.cpp and iter_move.pass.cpp).
+namespace adl {
+template <std::input_iterator Iter = int*, bool IsIterSwappable = true, bool IsNoExceptIterMoveable = true>
+struct IterMoveIterSwapTestRangeIterator
+ : InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, Iter, false> {
+ int* counter_{nullptr};
+
+ using InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, Iter, false>::
+ InputIter;
+
+ constexpr IterMoveIterSwapTestRangeIterator(Iter value, int* counter)
+ : InputIter<IterMoveIterSwapTestRangeIterator<Iter, IsIterSwappable, IsNoExceptIterMoveable>, Iter, false>(value),
+ counter_(counter) {}
+
+ friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u) noexcept
+ requires IsIterSwappable && IsNoExceptIterMoveable
+ {
+ (*t.counter_)++;
+ (*u.counter_)++;
+ std::swap(*t.value_, *u.value_);
+ }
+
+ friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u)
+ requires IsIterSwappable && (!IsNoExceptIterMoveable)
+ {
+ (*t.counter_)++;
+ (*u.counter_)++;
+ std::swap(*t.value_, *u.value_);
+ }
+
+ friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t)
+ requires(!IsNoExceptIterMoveable)
+ {
+ (*t.counter_)++;
+ return *t.value_;
+ }
+ friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t) noexcept
+ requires IsNoExceptIterMoveable
+ {
+ (*t.counter_)++;
+ return *t.value_;
+ }
+};
+} // namespace adl
+
+template <typename Iter = int*, bool IsSwappable = true, bool IsNoExcept = true>
+struct IterMoveIterSwapTestRange : std::ranges::view_base {
+ adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> begin_;
+ adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> end_;
+ constexpr IterMoveIterSwapTestRange(const Iter& begin, const Iter& end, int* counter)
+ : begin_(adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept>(begin, counter)),
+ end_(adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept>(end, counter)) {}
+ constexpr adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> begin() const { return begin_; }
+ constexpr adl::IterMoveIterSwapTestRangeIterator<Iter, IsSwappable, IsNoExcept> end() const { return end_; }
+};
+
+// Views
+
+// Depending upon configuration, ViewOrRange is either a View or not.
+template <bool IsView>
+struct MaybeView {};
+template <>
+struct MaybeView<true> : std::ranges::view_base {};
+
+template <std::input_iterator Iter,
+ std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+ bool IsSized = false,
+ bool IsView = false,
+ bool IsCopyable = false >
+ requires((!IsSized) || (IsSized && IterDifferable<Iter>))
+struct BasicTestViewOrRange : MaybeView<IsView> {
+ Iter begin_{};
+ Iter end_{};
+
+ constexpr BasicTestViewOrRange(const Iter& b, const Iter& e) : begin_(b), end_(e) {}
+
+ constexpr Iter begin() { return begin_; }
+ constexpr Iter begin() const { return begin_; }
+ constexpr Sent end() { return Sent{end_}; }
+ constexpr Sent end() const { return Sent{end_}; }
+
+ constexpr auto size() const
+ requires IsSized
+ {
+ return begin_ - end_;
+ }
+
+ constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other) = default;
+ constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
+
+ constexpr BasicTestViewOrRange(const BasicTestViewOrRange&)
+ requires(!IsCopyable)
+ = delete;
+ constexpr BasicTestViewOrRange(const BasicTestViewOrRange&)
+ requires IsCopyable
+ = default;
+
+ constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&)
+ requires(!IsCopyable)
+ = delete;
+ constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&)
+ requires IsCopyable
+ = default;
+};
+
+template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+ requires((!IsSized) || (IsSized && IterDifferable<Iter>))
+using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+
+template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+
+static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
+static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
+ sentinel_wrapper<cpp17_input_iterator<int*>>,
+ false>>);
+
+static_assert(std::copyable<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
+template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
+using CopyableView = MaybeCopyableAlwaysMoveableView<Iter, Sent>;
+static_assert(std::copyable<CopyableView<cpp17_input_iterator<int*>>>);
+
+template <class Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
+using MoveOnlyView = MaybeCopyableAlwaysMoveableView<Iter, Sent, false>;
+static_assert(!std::copyable<MoveOnlyView<cpp17_input_iterator<int*>>>);
+
+template <bool IsSimple, bool IsConst = IsSimple, bool IsCommon = true, bool IsSized = false>
+struct MaybeConstCommonSimpleView : std::ranges::view_base {
+ int* begin();
+ int* begin() const
+ requires(IsConst && IsSimple);
+ double* begin() const
+ requires(IsConst && !IsSimple);
+
+ int* end()
+ requires(IsCommon);
+ void* end()
+ requires(!IsCommon);
+
+ int* end() const
+ requires(IsConst && IsCommon && IsSimple);
+ double* end() const
+ requires(IsConst && IsCommon && !IsSimple);
+
+ void* end() const
+ requires(IsConst && !IsCommon);
+
+ size_t size() const
+ requires(IsSized);
+};
+
+using UnSimpleNoConstCommonView = MaybeConstCommonSimpleView<false, false, true>;
+using UnSimpleConstView = MaybeConstCommonSimpleView<false, true, true>;
+using UnsimpleUnCommonConstView = MaybeConstCommonSimpleView<false, true, false>;
+using SimpleUnCommonConstView = MaybeConstCommonSimpleView<true, true, false>;
+using SimpleCommonConstView = MaybeConstCommonSimpleView<true, true, true>;
+using SimpleNoConstSizedCommonView = MaybeConstCommonSimpleView<true, false, true, true>;
+
+// Don't move/hold the iterator itself, copy/hold the base
+// of that iterator and reconstruct the iterator on demand.
+// May result in aliasing (if, e.g., Iterator is an iterator
+// over int *).
+template <class Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
+struct ViewOverNonCopyableIterator : std::ranges::view_base {
+ constexpr explicit ViewOverNonCopyableIterator(Iter it, Sent sent) : it_(base(it)), sent_(base(sent)) {}
+
+ ViewOverNonCopyableIterator(ViewOverNonCopyableIterator&&) = default;
+ ViewOverNonCopyableIterator& operator=(ViewOverNonCopyableIterator&&) = default;
+
+ constexpr Iter begin() const { return Iter(it_); }
+ constexpr Sent end() const { return Sent(sent_); }
+
+private:
+ decltype(base(std::declval<Iter>())) it_;
+ decltype(base(std::declval<Sent>())) sent_;
+};
+
+// Ranges
+
+template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+ requires((!IsSized) || (IsSized && IterDifferable<Iter>))
+using BasicTestRange = BasicTestViewOrRange<Iter, Sent, IsSized, false>;
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..650c51c13f445 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1160,6 +1160,11 @@ def add_version_header(tc):
"values": {"c++23": 202106},
"headers": ["algorithm"],
},
+ {
+ "name": "__cpp_lib_ranges_stride",
+ "values": {"c++23": 202207},
+ "headers": ["ranges"],
+ },
{
"name": "__cpp_lib_ranges_to_container",
"values": {"c++23": 202202},
>From 09addafc38a3f27180dddc54b26690a3ea071684 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 13:46:20 -0500
Subject: [PATCH 2/9] Slip to LLVM 23 release notes
---
libcxx/docs/ReleaseNotes/22.rst | 1 -
libcxx/docs/ReleaseNotes/23.rst | 2 ++
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 26c413c29b3d1..4d56c82a53170 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -56,7 +56,6 @@ Implemented Papers
- P3567R2: ``flat_meow`` Fixes (`Github <https://llvm.org/PR162022>`__)
- P3836R2: Make ``optional<T&>`` trivially copyable (`Github <https://llvm.org/PR171275>`__)
- P1789R3: Library Support for Expansion Statements (`Github <https://llvm.org/PR167184>`__)
-- P1899: ``stride_view`` (`Github <https://github.com/llvm/llvm-project/issues/105198>` __)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 73f5984768592..a9887bb286e9e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 23.0.0?
Implemented Papers
------------------
+- P1899: ``stride_view`` (`Github <https://github.com/llvm/llvm-project/issues/105198>` __)
+
Improvements and New Features
-----------------------------
>From 78974b16fc807d1a78e176a0ab48e9fa7bec18c8 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 13:53:53 -0500
Subject: [PATCH 3/9] Add tests for .end()
---
.../{dereference.pass.cpp => dereference.assert.pass.cpp} | 6 ++++++
.../{increment.pass.cpp => increment.assert.pass.cpp} | 8 ++++++++
2 files changed, 14 insertions(+)
rename libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/{dereference.pass.cpp => dereference.assert.pass.cpp} (83%)
rename libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/{increment.pass.cpp => increment.assert.pass.cpp} (80%)
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
similarity index 83%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
index 98d62eeef8f26..9d75f1bdd072d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
@@ -30,5 +30,11 @@ int main(int, char**) {
++it;
TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end.");
}
+ {
+ int range[] = {1, 2, 3};
+ auto view = std::ranges::views::stride(range, 4);
+ auto it = view.end();
+ TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end.");
+ }
return 0;
}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
similarity index 80%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
index bc328b9bf92bc..8e2c2c1eee569 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
@@ -41,5 +41,13 @@ int main(int, char**) {
TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
}
+ {
+ int range[] = {1, 2, 3};
+ using Base = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto view = std::ranges::views::stride(Base(forward_iterator(range), forward_iterator(range + 3)), 3);
+ auto it = view.end();
+ TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
+ TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
+ }
return 0;
}
>From 5bb5ae0209ad0ccac8f049af388b6f4de743f0d4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 13:54:55 -0500
Subject: [PATCH 4/9] Rename assertion test
---
...or_plus_equal.pass.cpp => operator_plus_equal.assert.pass.cpp} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/{operator_plus_equal.pass.cpp => operator_plus_equal.assert.pass.cpp} (100%)
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
similarity index 100%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
>From be1fa90f7a5e1df98b5d1a0292e5f99917569b90 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 14:13:38 -0500
Subject: [PATCH 5/9] Address review comments in tests for adaptors
---
.../range.stride.view/adaptor.pass.cpp | 92 ++++++-------------
1 file changed, 29 insertions(+), 63 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
index fd4497254275e..cd3b58d1f2011 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -43,80 +43,46 @@ constexpr bool test() {
// Test that `std::views::stride` is a range adaptor.
// Check various forms of
- // view | stride
- // two tests: first with stride of 1; second with stride of 2.
+ // Test `view | views::stride`
{
- using View = BasicTestView<cpp17_input_iterator<int*>>;
- auto view = make_input_view(arr, arr + N);
- std::same_as<std::ranges::stride_view<View>> decltype(auto) strided = view | std::views::stride(1);
- auto strided_iter = strided.begin();
-
- // Check that the begin() iter views arr[0]
- assert(*strided_iter == arr[0]);
-
- // Check that the strided_iter, after advancing it 2 * 1 steps, views arr[2].
- std::ranges::advance(strided_iter, 2);
- assert(*strided_iter == arr[2]);
- }
- {
- using View = BasicTestView<cpp17_input_iterator<int*>>;
- auto view = make_input_view(arr, arr + N);
- std::same_as<std::ranges::stride_view<View>> decltype(auto) strided = view | std::views::stride(2);
- auto strided_iter = strided.begin();
-
- assert(*strided_iter == arr[0]);
- std::ranges::advance(strided_iter, 1);
- assert(*strided_iter == arr[2]);
+ using View = std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>;
+ auto view = make_input_view(arr, arr + N);
+ std::same_as<View> decltype(auto) result = view | std::views::stride(2);
+ auto it = result.begin();
+
+ assert(*it == arr[0]);
+ std::ranges::advance(it, 1);
+ assert(*it == arr[2]);
}
- // adaptor | stride
- // two tests: first with stride of 1; second with stride of 2.
- const auto i2 = [](int i) { return i * 2; };
+ // Test `adaptor | views::stride`
+ auto twice = [](int i) { return i * 2; };
{
- auto view = make_input_view(arr, arr + N);
- const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(1);
+ using View = std::ranges::stride_view<
+ std::ranges::transform_view<BasicTestView<cpp17_input_iterator<int*>>, decltype(twice)>>;
+ auto view = make_input_view(arr, arr + N);
+ const auto partial = std::views::transform(twice) | std::views::stride(2);
- auto transform_stride_applied = transform_stride_partial(view);
- auto transform_stride_applied_iter = transform_stride_applied.begin();
+ std::same_as<View> decltype(auto) result = partial(view);
+ auto it = result.begin();
- assert(*transform_stride_applied_iter == i2(arr[0]));
- std::ranges::advance(transform_stride_applied_iter, 2);
- assert(*transform_stride_applied_iter == i2(arr[2]));
+ assert(*it == twice(arr[0]));
+ std::ranges::advance(it, 1);
+ assert(*it == twice(arr[2]));
}
+ // Test `views::stride | adaptor`
{
- auto view = make_input_view(arr, arr + N);
- const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(2);
-
- const auto transform_stride_applied = transform_stride_partial(view);
- auto transform_stride_applied_iter = transform_stride_applied.begin();
-
- assert(*transform_stride_applied_iter == i2(arr[0]));
- std::ranges::advance(transform_stride_applied_iter, 1);
- assert(*transform_stride_applied_iter == i2(arr[2]));
- }
-
- // stride | adaptor
- // two tests: first with stride of 1; second with stride of 2.
- {
- auto view = make_input_view(arr, arr + N);
- const auto stride_transform = std::views::stride(view, 1) | std::views::transform(i2);
-
- auto stride_transform_iter = stride_transform.begin();
-
- assert(*stride_transform_iter == i2(arr[0]));
- std::ranges::advance(stride_transform_iter, 2);
- assert(*stride_transform_iter == i2(arr[2]));
- }
- {
- auto view = make_input_view(arr, arr + N);
- const auto stride_transform = std::views::stride(view, 2) | std::views::transform(i2);
+ using View = std::ranges::transform_view< std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>,
+ decltype(twice)>;
+ auto view = make_input_view(arr, arr + N);
+ std::same_as<View> decltype(auto) result = std::views::stride(view, 2) | std::views::transform(twice);
- auto stride_transform_iter = stride_transform.begin();
+ auto it = result.begin();
- assert(*stride_transform_iter == i2(arr[0]));
- std::ranges::advance(stride_transform_iter, 1);
- assert(*stride_transform_iter == i2(arr[2]));
+ assert(*it == twice(arr[0]));
+ std::ranges::advance(it, 1);
+ assert(*it == twice(arr[2]));
}
// Check SFINAE friendliness
>From 3c46dd7049f3331beb915015a552586a4f7bedf2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 14:14:02 -0500
Subject: [PATCH 6/9] Snake case comment in test
---
.../range.adaptors/range.stride.view/base.pass.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
index 4a9c4647e928a..8e065c5937370 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -18,7 +18,7 @@
#include "types.h"
template <typename T>
-constexpr bool hasLValueQualifiedBase(T&& t) {
+constexpr bool has_lvalue_qualified_base(T&& t) {
// Thanks to forwarding references, t's type is
// preserved from the caller. No matter the type of
// the argument, when it is used here, t is an l value
@@ -41,7 +41,7 @@ constexpr bool test() {
const auto str(std::ranges::stride_view<CopyableInputView>(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
- static_assert(hasLValueQualifiedBase(str));
+ static_assert(has_lvalue_qualified_base(str));
std::same_as<CopyableInputView> decltype(auto) s = str.base();
assert(*s.begin() == *buff);
@@ -55,7 +55,7 @@ constexpr bool test() {
std::same_as<CopyableInputView> decltype(auto) s = str.base();
assert(*s.begin() == *buff);
- static_assert(hasLValueQualifiedBase(str));
+ static_assert(has_lvalue_qualified_base(str));
}
// r-value ref qualified
@@ -64,7 +64,7 @@ constexpr bool test() {
auto str(std::ranges::stride_view<CopyableInputView>(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
- static_assert(hasLValueQualifiedBase(str));
+ static_assert(has_lvalue_qualified_base(str));
std::same_as<CopyableInputView> decltype(auto) s = std::move(str).base();
assert(*s.begin() == *buff);
@@ -75,7 +75,7 @@ constexpr bool test() {
const auto str_a(std::ranges::stride_view<CopyableInputView>(
CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
- static_assert(hasLValueQualifiedBase(str_a));
+ static_assert(has_lvalue_qualified_base(str_a));
std::same_as<CopyableInputView> decltype(auto) s = std::move(str_a).base();
assert(*s.begin() == *buff);
@@ -89,7 +89,7 @@ constexpr bool test() {
// Because the base of the stride view is move only,
// the const & version is not applicable and, therefore,
// there is no l-value qualified base method.
- static_assert(!hasLValueQualifiedBase(str));
+ static_assert(!has_lvalue_qualified_base(str));
std::same_as<MoveOnlyInputView> decltype(auto) s = std::move(str).base();
assert(*s.begin() == *buff);
>From 08710cf10427ae93ddab31f09f222ad2fd69d562 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jan 2026 14:18:56 -0500
Subject: [PATCH 7/9] Apply review comments in base.pass.cpp:
---
.../range.stride.view/base.pass.cpp | 40 +++++++++----------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
index 8e065c5937370..1d5e89c4684bf 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -38,60 +38,60 @@ constexpr bool test() {
// l-value ref qualified
// const &
{
- const auto str(std::ranges::stride_view<CopyableInputView>(
- CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+ const std::ranges::stride_view<CopyableInputView> view(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
- static_assert(has_lvalue_qualified_base(str));
+ static_assert(has_lvalue_qualified_base(view));
- std::same_as<CopyableInputView> decltype(auto) s = str.base();
+ std::same_as<CopyableInputView> decltype(auto) s = view.base();
assert(*s.begin() == *buff);
}
// &
{
- auto str(std::ranges::stride_view<CopyableInputView>(
- CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+ std::ranges::stride_view<CopyableInputView> view(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
- std::same_as<CopyableInputView> decltype(auto) s = str.base();
+ std::same_as<CopyableInputView> decltype(auto) s = view.base();
assert(*s.begin() == *buff);
- static_assert(has_lvalue_qualified_base(str));
+ static_assert(has_lvalue_qualified_base(view));
}
// r-value ref qualified
// &&
{
- auto str(std::ranges::stride_view<CopyableInputView>(
- CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+ std::ranges::stride_view<CopyableInputView> view(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
- static_assert(has_lvalue_qualified_base(str));
+ static_assert(has_lvalue_qualified_base(view));
- std::same_as<CopyableInputView> decltype(auto) s = std::move(str).base();
+ std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
// const &&
{
- const auto str_a(std::ranges::stride_view<CopyableInputView>(
- CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+ const std::ranges::stride_view<CopyableInputView> view(
+ CopyableInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
- static_assert(has_lvalue_qualified_base(str_a));
+ static_assert(has_lvalue_qualified_base(view));
- std::same_as<CopyableInputView> decltype(auto) s = std::move(str_a).base();
+ std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
// &&
{
- auto str(std::ranges::stride_view<MoveOnlyInputView>(
- MoveOnlyInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1));
+ std::ranges::stride_view<MoveOnlyInputView> view(
+ MoveOnlyInputView(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + N)), 1);
// Because the base of the stride view is move only,
// the const & version is not applicable and, therefore,
// there is no l-value qualified base method.
- static_assert(!has_lvalue_qualified_base(str));
+ static_assert(!has_lvalue_qualified_base(view));
- std::same_as<MoveOnlyInputView> decltype(auto) s = std::move(str).base();
+ std::same_as<MoveOnlyInputView> decltype(auto) s = std::move(view).base();
assert(*s.begin() == *buff);
}
return true;
>From adc8da1a3ca18e345f8cea2570ea982797b99d4f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 20 Mar 2026 13:31:06 -0400
Subject: [PATCH 8/9] This patch should address the remaining comments.
It's difficult because of the vast number of outstanding comments on
the PR, but I verified with Claude which confirms that everything is
addressed.
I also regrouped a few test files for iterator operations that contained
a lot of duplicated code -- we strive to test each function in a separate
file, but there's also a tradeoff.
The changes were implemented using Claude, but I was driving and validating
at every step.
---
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/__ranges/stride_view.h | 6 +-
libcxx/include/module.modulemap.in | 8 +-
.../range.stride.view/ctor.assert.pass.cpp | 2 +-
.../iterator/dereference.assert.pass.cpp | 7 +-
.../iterator/increment.assert.pass.cpp | 32 +-
.../iterator/iterator.nodiscard.verify.cpp | 113 +----
.../operator_plus_equal.assert.pass.cpp | 2 +-
.../range.stride.view/nodiscard.verify.cpp | 58 +--
.../range.stride.view/adaptor.pass.cpp | 5 +-
.../range.stride.view/base.pass.cpp | 6 +-
.../range.stride.view/begin.pass.cpp | 68 ++-
.../borrowing.compile.pass.cpp | 2 +-
.../concept.compile.pass.cpp | 6 +-
.../range.stride.view/ctad.pass.cpp | 16 +-
.../range.stride.view/ctor.pass.cpp | 23 +-
.../range.stride.view/end.pass.cpp | 145 ++++--
.../range.stride.view/iterator/base.pass.cpp | 156 +++----
.../iterator/compare.pass.cpp | 119 +++++
.../iterator/ctor.copy.pass.cpp | 50 +-
.../iterator/ctor.default.pass.cpp | 20 +-
.../iterator/decrement.pass.cpp | 22 +-
.../iterator/dereference.pass.cpp | 85 ++++
.../range.stride.view/iterator/equal.pass.cpp | 19 +-
.../iterator/greater_than.pass.cpp | 426 ------------------
.../iterator/increment.pass.cpp | 35 +-
.../iterator/iter_move.pass.cpp | 7 +-
.../iterator/iter_swap.pass.cpp | 9 +-
.../iterator/less_than.pass.cpp | 426 ------------------
.../range.stride.view/iterator/minus.pass.cpp | 186 ++++++++
.../iterator/minus_equal.pass.cpp | 87 ++++
.../iterator/operator.pass.cpp | 426 ------------------
.../range.stride.view/iterator/plus.pass.cpp | 34 +-
.../iterator/plus_equal.pass.cpp | 33 +-
.../iterator/subscript.pass.cpp | 99 ++++
.../range.stride.view/size.pass.cpp | 5 +-
.../range.stride.view/stride.pass.cpp | 6 +-
37 files changed, 1042 insertions(+), 1709 deletions(-)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp
delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index fd99ff635aa00..d1b43cb0dd33a 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -58,7 +58,7 @@
"`P1223R5 <https://wg21.link/P1223R5>`__","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","2022-07 (Virtual)","|Complete|","19","`#105194 <https://github.com/llvm/llvm-project/issues/105194>`__",""
"`P1467R9 <https://wg21.link/P1467R9>`__","Extended ``floating-point`` types and standard names","2022-07 (Virtual)","","","`#105196 <https://github.com/llvm/llvm-project/issues/105196>`__",""
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","","`#105197 <https://github.com/llvm/llvm-project/issues/105197>`__",""
-"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","","`#105198 <https://github.com/llvm/llvm-project/issues/105198>`__",""
+"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","|Complete|","23","`#105198 <https://github.com/llvm/llvm-project/issues/105198>`__",""
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18","`#105199 <https://github.com/llvm/llvm-project/issues/105199>`__",""
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","`#105200 <https://github.com/llvm/llvm-project/issues/105200>`__","Only the part for ``zip_view`` is implemented."
"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","","`#105201 <https://github.com/llvm/llvm-project/issues/105201>`__",""
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 5ca398382948b..8d22725e76e0e 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -16,16 +16,12 @@
#include <__concepts/convertible_to.h>
#include <__concepts/derived_from.h>
#include <__concepts/equality_comparable.h>
-#include <__concepts/relation.h>
#include <__config>
#include <__functional/bind_back.h>
-#include <__functional/operations.h>
-#include <__functional/ranges_operations.h>
#include <__iterator/advance.h>
#include <__iterator/concepts.h>
#include <__iterator/default_sentinel.h>
#include <__iterator/distance.h>
-#include <__iterator/indirectly_comparable.h>
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
@@ -115,7 +111,7 @@ class stride_view : public view_interface<stride_view<_View>> {
if constexpr (common_range<const _View> && sized_range<const _View> && forward_range<const _View>) {
auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
return __iterator</*_Const=*/true>(this, ranges::end(__base_), __missing);
- } else if constexpr (common_range<_View> && !bidirectional_range<_View>) {
+ } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>) {
return __iterator</*_Const=*/true>(this, ranges::end(__base_), 0);
} else {
return default_sentinel;
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3b94428fc2ca0..cbc14593fc389 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1939,6 +1939,10 @@ module std [system] {
header "__ranges/split_view.h"
export std.functional.bind_back
}
+ module stride_view {
+ header "__ranges/stride_view.h"
+ export std.functional.bind_back
+ }
module subrange {
header "__ranges/subrange.h"
export std.ranges.subrange_fwd
@@ -1946,10 +1950,6 @@ module std [system] {
module subrange_fwd {
header "__fwd/subrange.h"
}
- module stride_view {
- header "__ranges/stride_view.h"
- export std.functional.bind_back
- }
module take_view {
header "__ranges/take_view.h"
export std.functional.bind_back
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
index 2b5a549b72d4e..e564891b15a15 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
index 9d75f1bdd072d..6884c27f5197c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
@@ -6,15 +6,16 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
-// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
// constexpr decltype(auto) operator*() const
-#include "check_assertion.h"
#include <ranges>
+#include "check_assertion.h"
+
int main(int, char**) {
{
int range[] = {1, 2, 3};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
index 8e2c2c1eee569..3aaf5268afa3e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
@@ -6,13 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
-// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
-// constexpr stride_view::<iterator>& operator++()
// constexpr __iterator& operator++()
-// constexpr void operator++(int) {
+// constexpr void operator++(int)
// constexpr __iterator operator++(int)
#include <ranges>
@@ -20,13 +19,24 @@
#include "check_assertion.h"
#include "test_iterators.h"
-#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h"
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+struct MinimalView : std::ranges::view_base {
+ Iter begin_;
+ Sent end_;
+
+ constexpr MinimalView(Iter b, Sent e) : begin_(b), end_(e) {}
+ MinimalView(MinimalView&&) = default;
+ MinimalView& operator=(MinimalView&&) = default;
+
+ constexpr Iter begin() const { return begin_; }
+ constexpr Sent end() const { return end_; }
+};
int main(int, char**) {
{
int range[] = {1, 2, 3};
- using Base = BasicTestView<cpp17_input_iterator<int*>>;
- auto view = std::ranges::views::stride(Base(cpp17_input_iterator(range), cpp17_input_iterator(range + 3)), 3);
+ using View = MinimalView<cpp17_input_iterator<int*>>;
+ auto view = std::ranges::views::stride(View(cpp17_input_iterator(range), sentinel_wrapper(cpp17_input_iterator(range + 3))), 3);
auto it = view.begin();
++it;
TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
@@ -34,8 +44,8 @@ int main(int, char**) {
}
{
int range[] = {1, 2, 3};
- using Base = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
- auto view = std::ranges::views::stride(Base(forward_iterator(range), forward_iterator(range + 3)), 3);
+ using View = MinimalView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto view = std::ranges::views::stride(View(forward_iterator(range), forward_iterator(range + 3)), 3);
auto it = view.begin();
++it;
TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
@@ -43,8 +53,8 @@ int main(int, char**) {
}
{
int range[] = {1, 2, 3};
- using Base = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
- auto view = std::ranges::views::stride(Base(forward_iterator(range), forward_iterator(range + 3)), 3);
+ using View = MinimalView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto view = std::ranges::views::stride(View(forward_iterator(range), forward_iterator(range + 3)), 3);
auto it = view.end();
TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end.");
TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
index 70fa3799f48c5..152779ec3da85 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
@@ -6,109 +6,40 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// Test that stride_view's iterator member functions are properly marked nodiscard.
#include <ranges>
#include <utility>
-#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h"
-
-void test_base_nodiscard() {
- {
- int range[] = {1, 2, 3};
- auto view = std::ranges::views::stride(range, 3);
- auto it = view.begin();
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- it.base();
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- std::move(it).base();
- }
-}
-
-void test_dereference_nodiscard() {
- {
- int range[] = {1, 2, 3};
- auto view = std::ranges::views::stride(range, 3);
- auto it = view.begin();
- ++it;
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- *std::as_const(it);
- }
-}
-
-constexpr bool test_iter_move_nodiscard() {
- {
- int a[] = {4, 3, 2, 1};
-
- int iter_move_counter(0);
- using View = IterMoveIterSwapTestRange<int*, true, true>;
- using StrideView = std::ranges::stride_view<View>;
- auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
-
- static_assert(std::is_same_v<int, decltype(std::ranges::iter_move(svb))>);
- static_assert(noexcept(std::ranges::iter_move(svb)));
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- std::ranges::iter_move(svb);
- }
- return true;
-}
-
-constexpr bool test_non_forward_operator_minus_nodiscard() {
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
- using Base = BasicTestView<SizedInputIter, SizedInputIter>;
-
- auto base_view_offset_zero = Base(SizedInputIter(arr), SizedInputIter(arr + 10));
- auto base_view_offset_one = Base(SizedInputIter(arr + 1), SizedInputIter(arr + 10));
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+void test() {
+ int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto sv = std::ranges::stride_view(std::ranges::ref_view(range), 3);
+ auto it = sv.begin();
+ auto it2 = sv.begin();
+ ++it2;
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- sv_one_offset_begin - sv_zero_offset_begin;
- return true;
-}
-
-constexpr bool test_forward_operator_minus_nodiscard() {
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
- using Base = BasicTestView<int*, int*>;
-
- auto base_view_offset_zero = Base(arr, arr + 10);
- auto base_view_offset_one = Base(arr + 1, arr + 10);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
+ it.base();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- sv_zero_offset_begin + 1;
+ std::move(it).base();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- 1 + sv_zero_offset_begin;
+ *std::as_const(it);
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- sv_one_offset_begin - 1;
+ it[0];
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- sv_one_offset_begin - sv_zero_offset_begin;
+ it + 1;
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- std::default_sentinel_t() - sv_zero_offset_begin;
+ 1 + it;
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- sv_zero_offset_begin - std::default_sentinel_t();
-
- return true;
-}
-
-void test_subscript_nodiscard() {
- {
- int range[] = {1, 2, 3};
- auto view = std::ranges::views::stride(range, 3);
- auto it = view.begin();
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- it[0];
- }
+ it2 - 1;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ it2 - it;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::default_sentinel_t() - it;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ it - std::default_sentinel_t();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::iter_move(it);
}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
index 710ff20efe66e..0a84436444cd5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}}
// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index ae1e154449617..5a4dc070505a4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -6,68 +6,28 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// Test that
-// std::view::stride()
-// std::ranges::stride_view::base()
-// std::ranges::stride_view::begin()
-// std::ranges::stride_view::end()
-// std::ranges::stride_view::size()
-// std::ranges::stride_view::stride()
-// are all marked nodiscard.
+// Test that stride_view's member functions are properly marked nodiscard.
#include <ranges>
-#include "../../../../std/ranges/range.adaptors/range.stride.view/types.h"
+void test() {
+ int range[] = {1, 2, 3};
+ auto sv = std::ranges::stride_view(range, 2);
-void test_base_nodiscard() {
- const int range[] = {1, 2, 3};
- auto sv = std::ranges::stride_view(range, 3);
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
sv.base();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::move(sv).base();
-}
-
-void test_begin_nodiscard() {
- const auto const_sv = std::views::stride(SimpleCommonConstView{}, 2);
- auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2);
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- const_sv.begin();
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- unsimple_sv.begin();
-}
-
-void test_views_stride_nodiscard() {
- const int range[] = {1, 2, 3};
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- std::views::stride(range, 2);
-}
-
-void test_end_nodiscard() {
- const int range[] = {1, 2, 3};
-
- const auto const_sv = std::views::stride(range, 2);
- auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2);
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- const_sv.end();
+ sv.begin();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- unsimple_sv.end();
-}
-
-void test_size_nodiscard() {
- auto sv = std::views::stride(SimpleNoConstSizedCommonView(), 2);
- const auto const_sv = std::views::stride(SimpleCommonConstView(), 2);
+ sv.end();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
sv.size();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- const_sv.size();
-}
-
-void test_stride_nodiscard() {
- const int range[] = {1, 2, 3};
- auto const_sv = std::views::stride(range, 2);
+ sv.stride();
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- const_sv.stride();
+ std::views::stride(range, 2);
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
index cd3b58d1f2011..703a7c2c634be 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -6,10 +6,11 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// std::views::stride_view
+// std::views::stride
+#include <cassert>
#include <ranges>
#include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
index 1d5e89c4684bf..b5a7930dd951e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -6,10 +6,10 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr _View base() const& requires copy_constructible<_View>;
-// constexpr _View base() &&;
+// constexpr V base() const& requires copy_constructible<V>;
+// constexpr V base() &&;
#include <cassert>
#include <ranges>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
index d892e11fc7a8b..8c21fcaebb482 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
@@ -6,14 +6,15 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr auto begin() requires(!__simple_view<_View>)
-// constexpr auto begin() const requires range<const _View>
+// constexpr auto begin() requires(!simple-view<V>)
+// constexpr auto begin() const requires range<const V>
// Note: Checks here are augmented by checks in
// iterator/ctor.copy.pass.cpp.
+#include <cassert>
#include <concepts>
#include <ranges>
@@ -46,14 +47,57 @@ static_assert(HasOnlyConstBegin<std::ranges::stride_view<SimpleCommonConstView>>
static_assert(HasConstAndNonConstBegin<std::ranges::stride_view<UnSimpleConstView>>);
constexpr bool test() {
- const auto unsimple_const_view = UnSimpleConstView();
- const auto sv_unsimple_const = std::ranges::stride_view(unsimple_const_view, 1);
- static_assert(std::same_as<decltype(*sv_unsimple_const.begin()), double&>);
-
- auto simple_const_view = SimpleCommonConstView();
- auto sv_simple_const = std::ranges::stride_view(simple_const_view, 1);
- static_assert(std::same_as<decltype(*sv_simple_const.begin()), int&>);
-
+ {
+ // Return type check for non-simple const view.
+ const auto v = UnSimpleConstView();
+ const auto sv = std::ranges::stride_view(v, 1);
+ static_assert(std::same_as<decltype(*sv.begin()), double&>);
+ }
+ {
+ // Return type check for simple const view.
+ auto v = SimpleCommonConstView();
+ auto sv = std::ranges::stride_view(v, 1);
+ static_assert(std::same_as<decltype(*sv.begin()), int&>);
+ }
+ {
+ // Verify begin() produces the first element with stride 1.
+ int data[] = {10, 20, 30, 40, 50};
+ auto v = BasicTestView<int*, int*>{data, data + 5};
+ auto sv = std::ranges::stride_view(v, 1);
+ assert(*sv.begin() == 10);
+ }
+ {
+ // Verify begin() produces the first element with stride 3.
+ int data[] = {10, 20, 30, 40, 50};
+ auto v = BasicTestView<int*, int*>{data, data + 5};
+ auto sv = std::ranges::stride_view(v, 3);
+ assert(*sv.begin() == 10);
+ }
+ {
+ // Verify iterating from begin with stride 2 produces correct elements.
+ int data[] = {1, 2, 3, 4, 5};
+ auto v = BasicTestView<int*, int*>{data, data + 5};
+ auto sv = std::ranges::stride_view(v, 2);
+ auto it = sv.begin();
+ assert(*it == 1);
+ ++it;
+ assert(*it == 3);
+ ++it;
+ assert(*it == 5);
+ ++it;
+ assert(it == sv.end());
+ }
+ {
+ // Verify begin on forward range.
+ int data[] = {100, 200, 300};
+ using FwdView = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto v = FwdView{forward_iterator(data), forward_iterator(data + 3)};
+ auto sv = std::ranges::stride_view(v, 2);
+ assert(*sv.begin() == 100);
+ auto it = sv.begin();
+ ++it;
+ assert(*it == 300);
+ }
return true;
}
@@ -61,4 +105,4 @@ int main(int, char**) {
test();
static_assert(test());
return 0;
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
index 4846f728c7011..cfadffbda28de 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// template<class V>
// inline constexpr bool enable_borrowed_range<stride_view<V>> = ranges::enable_borrowed_range<V>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
index ea60d282c422e..652dfcff3b0c3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
@@ -6,9 +6,11 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// template <input_range _View> requires view<_View>
+// template <input_range V> requires view<V>
+
+#include <ranges>
#include "almost_satisfies_types.h"
#include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
index f0f1943de1a2c..de193bed51da1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
@@ -6,15 +6,17 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// template <class _Range>
-// stride_view(_Range&&, range_difference_t<_Range>) -> stride_view<views::all_t<_Range>>;
+// template <class R>
+// stride_view(R&&, range_difference_t<R>) -> stride_view<views::all_t<R>>;
-#include "types.h"
+#include <cassert>
#include <concepts>
#include <ranges>
+#include "types.h"
+
struct View : std::ranges::view_base {
int* begin() const;
int* end() const;
@@ -25,7 +27,7 @@ struct Range {
int* end() const;
};
-constexpr bool testCTAD() {
+constexpr bool test() {
int a[] = {1, 2, 3, 4, 5};
using BaseRange = BasicTestRange<cpp17_input_iterator<int*>>;
@@ -89,8 +91,8 @@ constexpr bool testCTAD() {
}
int main(int, char**) {
- testCTAD();
- static_assert(testCTAD());
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
index 6b4859499d076..b41e5e809d232 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
@@ -6,10 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr explicit stride_view(_View, range_difference_t<_View>)
+// constexpr explicit stride_view(V base, range_difference_t<V> stride)
+#include <cassert>
+#include <ranges>
#include <type_traits>
#include "test_convertible.h"
@@ -38,6 +40,23 @@ constexpr bool test() {
// While we are here, make sure that the ctor captured the stride.
assert(mosv.stride() == 1);
}
+ {
+ // Verify salient properties after construction.
+ int arr[] = {10, 20, 30, 40, 50};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2);
+
+ assert(sv.stride() == 2);
+ assert(*sv.begin() == 10);
+
+ auto it = sv.begin();
+ ++it;
+ assert(*it == 30);
+ ++it;
+ assert(*it == 50);
+ ++it;
+ assert(it == sv.end());
+ }
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
index af6532f790a6a..5dd4f362576ee 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
@@ -6,19 +6,51 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr auto end() requires(!__simple_view<_View>)
-// constexpr auto end() const requires(range<const _View>)
+// constexpr auto end() requires(!simple-view<V>)
+// constexpr auto end() const requires(range<const V>)
// Note: Checks here are augmented by checks in
// iterator/ctor.copy.pass.cpp.
#include <ranges>
+#include <cassert>
+#include <type_traits>
#include "test_iterators.h"
+#include "test_macros.h"
#include "types.h"
+// A view that is a common forward range when const, but NOT a common range when non-const.
+struct CommonForwardOnlyWhenConst : std::ranges::view_base {
+ int* data_;
+ int size_;
+
+ constexpr CommonForwardOnlyWhenConst(int* d, int s) : data_(d), size_(s) {}
+
+ CommonForwardOnlyWhenConst(CommonForwardOnlyWhenConst&&) = default;
+ CommonForwardOnlyWhenConst& operator=(CommonForwardOnlyWhenConst&&) = default;
+
+ // Non-const: not a common range (iterator and sentinel are different types).
+ constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(data_); }
+ constexpr sentinel_wrapper<forward_iterator<int*>> end() {
+ return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(data_ + size_));
+ }
+
+ // Const: a common forward range (begin and end return the same type).
+ constexpr forward_iterator<const int*> begin() const { return forward_iterator<const int*>(data_); }
+ constexpr forward_iterator<const int*> end() const { return forward_iterator<const int*>(data_ + size_); }
+};
+
+static_assert(std::ranges::range<CommonForwardOnlyWhenConst>);
+static_assert(std::ranges::range<const CommonForwardOnlyWhenConst>);
+static_assert(std::ranges::forward_range<const CommonForwardOnlyWhenConst>);
+static_assert(!std::ranges::bidirectional_range<const CommonForwardOnlyWhenConst>);
+static_assert(std::ranges::common_range<const CommonForwardOnlyWhenConst>);
+static_assert(!std::ranges::common_range<CommonForwardOnlyWhenConst>);
+static_assert(std::ranges::view<CommonForwardOnlyWhenConst>);
+
template <class T>
concept HasConstEnd = requires(const T& ct) { ct.end(); };
@@ -39,73 +71,120 @@ static_assert(HasOnlyNonConstEnd<std::ranges::stride_view<UnSimpleNoConstCommonV
static_assert(HasOnlyConstEnd<std::ranges::stride_view<SimpleCommonConstView>>);
static_assert(HasConstAndNonConstEnd<std::ranges::stride_view<UnSimpleConstView>>);
-constexpr bool test_non_default_sentinel() {
+constexpr bool test() {
{
- const int data[3] = {1, 2, 3};
- // A const, simple, common-, sized- and forward-range
+ // A const, simple, common-, sized- and forward-range.
// Note: sized because it is possible to get a difference between its
// beginning and its end.
+ const int data[] = {1, 2, 3};
auto v = BasicTestView<const int*, const int*>{data, data + 3};
- auto sv = std::ranges::stride_view<BasicTestView<const int*, const int*>>(v, 1);
+ auto sv = std::ranges::stride_view(v, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
- }
+ // Verify actual end behavior: iterating reaches end.
+ auto it = sv.begin();
+ ++it; ++it; ++it;
+ assert(it == sv.end());
+ }
{
- int data[3] = {1, 2, 3};
// ForwardTestView is not sized and not bidirectional, but it is common.
// Note: It is not sized because BasicTestView has no member function named size (by default)
// and nor is it possible to get a difference between its beginning and its end.
+ int data[] = {1, 2, 3};
using ForwardTestView = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
-
auto v = ForwardTestView{forward_iterator(data), forward_iterator(data + 3)};
- auto sv = std::ranges::stride_view<ForwardTestView>(v, 1);
+ auto sv = std::ranges::stride_view(v, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
- }
+ auto it = sv.begin();
+ ++it; ++it; ++it;
+ assert(it == sv.end());
+ }
{
- // TODO: Start here.
+ // A non-const, non-simple, common-, sized- and forward-range.
static_assert(!simple_view<UnSimpleNoConstCommonView>);
static_assert(std::ranges::common_range<UnSimpleNoConstCommonView>);
static_assert(std::ranges::sized_range<UnSimpleNoConstCommonView>);
static_assert(std::ranges::forward_range<UnSimpleNoConstCommonView>);
- // An unconst, unsimple, common-, sized- and forward-range
- auto v = UnSimpleNoConstCommonView{};
- auto sv = std::ranges::stride_view<UnSimpleNoConstCommonView>(v, 1);
+ auto sv = std::ranges::stride_view<UnSimpleNoConstCommonView>(UnSimpleNoConstCommonView{}, 1);
static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
}
- return true;
-}
-
-constexpr bool test_default_sentinel() {
{
+ // Uncommon range -> returns default_sentinel.
static_assert(!simple_view<UnsimpleUnCommonConstView>);
static_assert(!std::ranges::common_range<UnsimpleUnCommonConstView>);
- static_assert(std::ranges::sized_range<UnSimpleConstView>);
- static_assert(std::ranges::forward_range<UnSimpleConstView>);
- auto v = UnsimpleUnCommonConstView{};
- auto sv = std::ranges::stride_view<UnsimpleUnCommonConstView>(v, 1);
+ auto sv = std::ranges::stride_view<UnsimpleUnCommonConstView>(UnsimpleUnCommonConstView{}, 1);
ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end()));
}
-
{
+ // Simple, uncommon range -> returns default_sentinel.
static_assert(simple_view<SimpleUnCommonConstView>);
static_assert(!std::ranges::common_range<SimpleUnCommonConstView>);
- auto v = SimpleUnCommonConstView{};
- auto sv = std::ranges::stride_view<SimpleUnCommonConstView>(v, 1);
-
+ auto sv = std::ranges::stride_view<SimpleUnCommonConstView>(SimpleUnCommonConstView{}, 1);
ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end()));
}
+ {
+ // Verify stride > 1 with end(): iterating produces correct elements and terminates.
+ int data[] = {10, 20, 30, 40, 50};
+ auto v = BasicTestView<int*, int*>{data, data + 5};
+ auto sv = std::ranges::stride_view(v, 2);
+
+ auto it = sv.begin();
+ assert(*it == 10);
+ ++it;
+ assert(*it == 30);
+ ++it;
+ assert(*it == 50);
+ ++it;
+ assert(it == sv.end());
+ }
+ {
+ // Verify end() with stride that doesn't evenly divide the range.
+ int data[] = {1, 2, 3, 4, 5, 6, 7};
+ auto v = BasicTestView<int*, int*>{data, data + 7};
+ auto sv = std::ranges::stride_view(v, 3);
+
+ auto it = sv.begin();
+ assert(*it == 1);
+ ++it;
+ assert(*it == 4);
+ ++it;
+ assert(*it == 7);
+ ++it;
+ assert(it == sv.end());
+ }
+ {
+ // Regression test: end() const should use common_range<const _View>, not common_range<_View>.
+ // CommonForwardOnlyWhenConst is common + forward-only when const, but NOT common when non-const.
+ // With the bug, end() const would incorrectly check common_range<_View> (false), skip the second
+ // branch, and return default_sentinel. With the fix, it checks common_range<const _View> (true)
+ // and returns an iterator.
+ int data[] = {1, 2, 3, 4, 5};
+ auto v = CommonForwardOnlyWhenConst(data, 5);
+ auto sv = std::ranges::stride_view(std::move(v), 2);
+ const auto& csv = sv;
+
+ // The key assertion: end() on the const stride_view must NOT return default_sentinel_t.
+ static_assert(!std::is_same_v<std::default_sentinel_t, decltype(csv.end())>);
+
+ // Verify iteration actually works and reaches end.
+ auto it = csv.begin();
+ assert(*it == 1);
+ ++it;
+ assert(*it == 3);
+ ++it;
+ assert(*it == 5);
+ ++it;
+ assert(it == csv.end());
+ }
return true;
}
int main(int, char**) {
- test_non_default_sentinel();
- test_default_sentinel();
- static_assert(test_non_default_sentinel());
- static_assert(test_default_sentinel());
-
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
index f8c6725b2e057..24052d1e00260 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
@@ -6,127 +6,93 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr iterator_t<_Base> const& base() const& noexcept
-// constexpr iterator_t<_Base> base() &&
+// constexpr const iterator_t<Base>& base() const& noexcept
+// constexpr iterator_t<Base> base() &&
+#include <cassert>
#include <ranges>
#include <type_traits>
#include "../types.h"
-constexpr bool base_noexcept() {
+constexpr bool test() {
{
+ // base() const& is noexcept; base() && is not.
int arr[] = {1, 2, 3};
auto stride = std::ranges::stride_view(arr, 1);
[[maybe_unused]] auto stride_iter = stride.begin();
- // Check that calling base on an iterator where this is an lvalue reference
- // is noexcept.
static_assert(noexcept(stride_iter.base()));
- // Calling base on an iterator where this is an rvalue reference may except.
static_assert(!noexcept((std::move(stride_iter).base())));
}
-
- return true;
-}
-
-constexpr bool base_const() {
{
+ // base() const& returns a const ref; base() && returns a non-const value.
int arr[] = {1, 2, 3};
auto stride = std::ranges::stride_view(arr, 1);
[[maybe_unused]] auto stride_iter = stride.begin();
- // Calling base on an iterator where this is lvalue returns a const ref to an iterator.
static_assert(std::is_const_v<std::remove_reference_t<decltype(stride_iter.base())>>);
- // Calling base on an iterator where this is an rvalue reference returns a non-const iterator.
static_assert(!std::is_const_v<decltype(std::move(stride_iter).base())>);
}
-
- return true;
-}
-
-constexpr bool base_move() {
- // Keep track of how many times the original iterator is moved
- // and/or copied during the test.
- int move_counter = 0;
- int copy_counter = 0;
-
- auto start = SizedInputIter();
- start.move_counter = &move_counter;
- start.copy_counter = ©_counter;
- auto stop = SizedInputIter();
-
- auto view = BasicTestView<SizedInputIter>{start, stop};
- assert(move_counter == 0);
- // One copies of _start_ occurs when it is copied to the basic test view's member variable.
- assert(copy_counter == 1);
-
- auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
- // There is a copy of _view_ made when it is passed by value.
- // There is a move done of _view_ when it is used as the initial value of __base.
- assert(move_counter == 1);
- assert(copy_counter == 2);
-
- auto svi = sv.begin();
- // Another copy of _start_ when begin uses the iterator to the first element
- // of the view underlying sv as the by-value parameter to the stride view iterator's
- // constructor.
- assert(copy_counter == 3);
- // Another move of __start_ happens right after that when it is std::move'd to
- // become the first __current of the iterator.
- assert(move_counter == 2);
-
- [[maybe_unused]] auto result = std::move(svi).base();
- // Ensure that base std::move'd the iterator and did not copy it.
- assert(move_counter == 3);
- assert(copy_counter == 3);
- return true;
-}
-
-constexpr bool base_copy() {
- // See base_move() for complete description of when/why
- // moves/copies take place..
- int move_counter = 0;
- int copy_counter = 0;
- auto start = SizedInputIter();
-
- start.move_counter = &move_counter;
- start.copy_counter = ©_counter;
- auto stop = SizedInputIter();
-
- auto view = BasicTestView<SizedInputIter>{start, stop};
- assert(move_counter == 0);
- assert(copy_counter == 1);
-
- auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
- assert(move_counter == 1);
- assert(copy_counter == 2);
-
- [[maybe_unused]] auto svi = sv.begin();
- assert(copy_counter == 3);
- assert(move_counter == 2);
-
- [[maybe_unused]] const SizedInputIter result = svi.base();
- // Ensure that base did _not_ std::move'd the iterator.
- assert(move_counter == 2);
- assert(copy_counter == 4);
-
+ {
+ // base() && moves the underlying iterator.
+ int move_counter = 0;
+ int copy_counter = 0;
+
+ auto start = SizedInputIter();
+ start.move_counter = &move_counter;
+ start.copy_counter = ©_counter;
+ auto stop = SizedInputIter();
+
+ auto view = BasicTestView<SizedInputIter>{start, stop};
+ assert(move_counter == 0);
+ assert(copy_counter == 1);
+
+ auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
+ assert(move_counter == 1);
+ assert(copy_counter == 2);
+
+ auto svi = sv.begin();
+ assert(copy_counter == 3);
+ assert(move_counter == 2);
+
+ [[maybe_unused]] auto result = std::move(svi).base();
+ assert(move_counter == 3);
+ assert(copy_counter == 3);
+ }
+ {
+ // base() const& copies the underlying iterator.
+ int move_counter = 0;
+ int copy_counter = 0;
+ auto start = SizedInputIter();
+
+ start.move_counter = &move_counter;
+ start.copy_counter = ©_counter;
+ auto stop = SizedInputIter();
+
+ auto view = BasicTestView<SizedInputIter>{start, stop};
+ assert(move_counter == 0);
+ assert(copy_counter == 1);
+
+ auto sv = std::ranges::stride_view<BasicTestView<SizedInputIter>>(view, 1);
+ assert(move_counter == 1);
+ assert(copy_counter == 2);
+
+ [[maybe_unused]] auto svi = sv.begin();
+ assert(copy_counter == 3);
+ assert(move_counter == 2);
+
+ [[maybe_unused]] const SizedInputIter base_result = svi.base();
+ assert(move_counter == 2);
+ assert(copy_counter == 4);
+ }
return true;
}
int main(int, char**) {
- base_noexcept();
- static_assert(base_noexcept());
-
- base_const();
- static_assert(base_const());
-
- base_move();
- static_assert(base_move());
-
- base_copy();
- static_assert(base_copy());
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp
new file mode 100644
index 0000000000000..da7f361d73d7e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// friend constexpr bool operator<(const iterator& x, const iterator& y)
+// friend constexpr bool operator>(const iterator& x, const iterator& y)
+// friend constexpr bool operator<=(const iterator& x, const iterator& y)
+// friend constexpr bool operator>=(const iterator& x, const iterator& y)
+// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+
+#include <cassert>
+#include <functional>
+#include <ranges>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+// Input view: no relational comparisons.
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideInputIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+static_assert(!std::is_invocable_v<std::less<>, StrideInputIter, StrideInputIter>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideInputIter, StrideInputIter>);
+static_assert(!std::is_invocable_v<std::less_equal<>, StrideInputIter, StrideInputIter>);
+static_assert(!std::is_invocable_v<std::greater_equal<>, StrideInputIter, StrideInputIter>);
+
+// Forward view: no relational comparisons.
+using FwdView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideFwdIter = std::ranges::iterator_t<std::ranges::stride_view<FwdView>>;
+static_assert(!std::is_invocable_v<std::less<>, StrideFwdIter, StrideFwdIter>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideFwdIter, StrideFwdIter>);
+static_assert(!std::is_invocable_v<std::less_equal<>, StrideFwdIter, StrideFwdIter>);
+static_assert(!std::is_invocable_v<std::greater_equal<>, StrideFwdIter, StrideFwdIter>);
+
+// Bidirectional view: no relational comparisons.
+using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideBidirIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
+static_assert(!std::is_invocable_v<std::less<>, StrideBidirIter, StrideBidirIter>);
+static_assert(!std::is_invocable_v<std::greater<>, StrideBidirIter, StrideBidirIter>);
+static_assert(!std::is_invocable_v<std::less_equal<>, StrideBidirIter, StrideBidirIter>);
+static_assert(!std::is_invocable_v<std::greater_equal<>, StrideBidirIter, StrideBidirIter>);
+
+// Random access view: all relational comparisons available.
+using RAView = BasicTestView<random_access_iterator<int*>>;
+using StrideRAIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
+static_assert(std::is_invocable_v<std::less<>, StrideRAIter, StrideRAIter>);
+static_assert(std::is_invocable_v<std::greater<>, StrideRAIter, StrideRAIter>);
+static_assert(std::is_invocable_v<std::less_equal<>, StrideRAIter, StrideRAIter>);
+static_assert(std::is_invocable_v<std::greater_equal<>, StrideRAIter, StrideRAIter>);
+
+// three_way_comparable when the base iterator is three_way_comparable.
+using ThreeWayView = BasicTestView<three_way_contiguous_iterator<int*>>;
+using StrideThreeWayIter = std::ranges::iterator_t<std::ranges::stride_view<ThreeWayView>>;
+static_assert(std::three_way_comparable<StrideThreeWayIter>);
+
+// Not three_way_comparable when the base is not.
+using EqualOnlyView = BasicTestView<cpp17_input_iterator<int*>>;
+using StrideEqualOnlyIter = std::ranges::iterator_t<std::ranges::stride_view<EqualOnlyView>>;
+static_assert(!std::three_way_comparable<StrideEqualOnlyIter>);
+
+constexpr bool test() {
+ {
+ // <, >, <=, >= on random access range.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 9), 3);
+
+ auto a = sv.begin(); // index 0
+ auto b = sv.begin();
+ ++b; // index 3
+ auto c = sv.begin();
+ ++c; ++c; // index 6
+
+ assert(a < b);
+ assert(b < c);
+ assert(!(b < a));
+ assert(!(a < a));
+
+ assert(b > a);
+ assert(c > b);
+ assert(!(a > b));
+ assert(!(a > a));
+
+ assert(a <= b);
+ assert(a <= a);
+ assert(!(b <= a));
+
+ assert(b >= a);
+ assert(a >= a);
+ assert(!(a >= b));
+ }
+ {
+ // <=> on three_way_comparable base.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7};
+ using Base = BasicTestView<three_way_contiguous_iterator<int*>, three_way_contiguous_iterator<int*>>;
+ auto sv = std::ranges::stride_view(Base(three_way_contiguous_iterator(arr),
+ three_way_contiguous_iterator(arr + 7)), 2);
+
+ auto a = sv.begin();
+ auto b = sv.begin();
+ ++b;
+
+ assert((a <=> b) == std::strong_ordering::less);
+ assert((b <=> a) == std::strong_ordering::greater);
+ assert((a <=> a) == std::strong_ordering::equal);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
index 8667a581d5d51..5bfd19eebdd7c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
@@ -6,11 +6,11 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr __iterator(__iterator<!_Const> __i)
-// requires _Const && convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
-// convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+// constexpr iterator(iterator<!Const> i)
+// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>> &&
+// convertible_to<sentinel_t<V>, sentinel_t<Base>>
#include <ranges>
@@ -102,10 +102,10 @@ struct NotSimpleViewConstIterEnd : InputIter<NotSimpleViewConstIterEnd<CopyConve
};
/*
- * Goal: We will need a way to get a stride_view<true>::__iterator and a
- * stride_view<false>::__iterator because those are the two possible types
- * of the stride_view::__iterator constructor. The template value is determined
- * by whether the stride_view::__iterator is derivative of a stride_view over a
+ * Goal: We will need a way to get a stride_view::iterator<true> and a
+ * stride_view::iterator<false> because those are the two possible types
+ * of the stride_view::iterator constructor. The template value is determined
+ * by whether the stride_view::iterator is derivative of a stride_view over a
* view that is simple.
*
* So, first things first, we need to build a stride_view over a (non-)simple view.
@@ -117,10 +117,10 @@ struct NotSimpleViewConstIterEnd : InputIter<NotSimpleViewConstIterEnd<CopyConve
* for those conditions individually. We parameterize with a template to decide
* whether to
* 1. enable converting constructors between the non-const and the const version.
- * That feature is important for testing the stride_view::__iterator<true> converting
- * constructor from a stride_view::_iterator<false> iterator.
+ * That feature is important for testing the stride_view::iterator<true> converting
+ * constructor from a stride_view::iterator<false> iterator.
* 2. enable copyability. That feature is important for testing whether the requirement
- * the that copy constructor for the stride_view::__iterator<false> type actually moves
+ * that the copy constructor for the stride_view::iterator<false> type actually moves
* the underlying iterator.
*/
template <bool CopyConvertible = false, bool MoveConvertible = true>
@@ -138,15 +138,14 @@ struct NotSimpleViewDifferentEnd : std::ranges::view_base {
constexpr NotSimpleViewIterEnd end() { return {}; }
};
-constexpr bool non_simple_view_iter_ctor_test() {
- using NotSimpleStrideView = std::ranges::stride_view<NotSimpleViewDifferentBegin<false>>;
- using NotSimpleStrideViewIter = std::ranges::iterator_t<NotSimpleStrideView>;
- using NotSimpleStrideViewIterConst = std::ranges::iterator_t<const NotSimpleStrideView>;
- static_assert(!std::is_same_v<NotSimpleStrideViewIterConst, NotSimpleStrideViewIter>);
- return true;
-}
+constexpr bool test() {
+ {
+ using NotSimpleStrideView = std::ranges::stride_view<NotSimpleViewDifferentBegin<false>>;
+ using NotSimpleStrideViewIter = std::ranges::iterator_t<NotSimpleStrideView>;
+ using NotSimpleStrideViewIterConst = std::ranges::iterator_t<const NotSimpleStrideView>;
+ static_assert(!std::is_same_v<NotSimpleStrideViewIterConst, NotSimpleStrideViewIter>);
+ }
-constexpr bool non_const_iterator_copy_ctor() {
// All tests share the following general configuration.
//
// Instantiate a stride view StrideView over a non-simple view (NotSimpleViewBeingStrided) whose
@@ -263,9 +262,9 @@ constexpr bool non_const_iterator_copy_ctor() {
// Now, do what we wanted the whole time: make sure that we can copy construct a
// stride_view::iterator<true> from a stride_view::iterator<false>. The copy
- // constructor requires that the new __current_ StrideViewConstIter (type
+ // constructor requires that the new const iterator (type
// NotSimpleViewBeingStridedConstIterator) be constructable
- // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator).
+ // from the moved str.begin() iterator (type NotSimpleViewBeingStridedConstIterator).
StrideViewConstIter iterator_copy{str.begin()};
}
@@ -358,17 +357,16 @@ constexpr bool non_const_iterator_copy_ctor() {
// Now, do what we wanted the whole time: make sure that we can copy construct a
// stride_view::iterator<true> from a stride_view::iterator<false>. The copy
- // constructor requires that the new __current_ StrideViewConstIter (type
+ // constructor requires that the new const iterator (type
// NotSimpleViewBeingStridedConstIterator) be constructable
- // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator).
+ // from the moved str.begin() iterator (type NotSimpleViewBeingStridedConstIterator).
StrideViewConstIter iterator_copy{str.begin()};
}
return true;
}
int main(int, char**) {
- non_simple_view_iter_ctor_test();
- static_assert(non_simple_view_iter_ctor_test());
-
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
index 9bf719e470d53..c410644fe8e7a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -6,9 +6,9 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// __iterator() requires default_initializable<iterator_t<_Base>> = default;
+// iterator() requires default_initializable<iterator_t<Base>> = default;
#include <ranges>
@@ -36,4 +36,18 @@ static_assert(!std::is_default_constructible< std::ranges::iterator_t<ViewWithNo
static_assert(std::is_default_constructible<
std::ranges::iterator_t< std::ranges::stride_view<std::ranges::ref_view<const int[3]>>>>());
-int main(int, char**) { return 0; }
+constexpr bool test() {
+ {
+ // Default construct an iterator over a default-constructible base.
+ using SV = std::ranges::stride_view<std::ranges::ref_view<const int[3]>>;
+ using It = std::ranges::iterator_t<SV>;
+ [[maybe_unused]] It it{};
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
index ceb700b5d1374..131743d582055 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
@@ -6,11 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr __iterator& operator--()
-// constexpr __iterator operator--(int)
+// constexpr iterator& operator--()
+// constexpr iterator operator--(int)
+#include <cassert>
#include <ranges>
#include <vector>
@@ -83,11 +84,16 @@ constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) {
return true;
}
-int main(int, char**) {
- constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- test_operator_decrement(vec.begin(), vec.end(), 3);
- test_operator_decrement(arr, arr + 11, 3);
+constexpr bool test() {
+ {
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_operator_decrement(arr, arr + 11, 3);
+ }
+ return true;
+}
+int main(int, char**) {
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
new file mode 100644
index 0000000000000..a04a8c22b6db2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr decltype(auto) operator*() const
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+constexpr bool test() {
+ {
+ // Dereference with stride 1.
+ int arr[] = {10, 20, 30};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 3), 1);
+ auto it = sv.begin();
+
+ assert(*it == 10);
+ ++it;
+ assert(*it == 20);
+ ++it;
+ assert(*it == 30);
+ }
+ {
+ // Dereference with stride 2.
+ int arr[] = {10, 20, 30, 40, 50};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2);
+ auto it = sv.begin();
+
+ assert(*it == 10);
+ ++it;
+ assert(*it == 30);
+ ++it;
+ assert(*it == 50);
+ }
+ {
+ // Dereference with stride larger than range.
+ int arr[] = {42, 99};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 2), 5);
+ auto it = sv.begin();
+
+ assert(*it == 42);
+ }
+ {
+ // Dereference returns a reference that can be assigned through.
+ int arr[] = {1, 2, 3, 4, 5};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2);
+ auto it = sv.begin();
+
+ *it = 100;
+ assert(arr[0] == 100);
+ ++it;
+ *it = 200;
+ assert(arr[2] == 200);
+ }
+ {
+ // Dereference on a forward range with stride 3.
+ int arr[] = {5, 10, 15, 20, 25, 30};
+ using Base = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+ auto sv = std::ranges::stride_view(Base(forward_iterator(arr), forward_iterator(arr + 6)), 3);
+ auto it = sv.begin();
+ assert(*it == 5);
+ ++it;
+ assert(*it == 20);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
index 33faa77d1ff47..4bf2f04e64f8c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -6,17 +6,30 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
-// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+// friend constexpr bool operator==(const iterator& x, default_sentinel_t)
+// friend constexpr bool operator==(const iterator& x, const iterator& y)
#include <cassert>
+#include <concepts>
#include <ranges>
#include "../types.h"
#include "test_iterators.h"
+// A stride_view iterator over an equality_comparable base should itself be equality_comparable.
+using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
+using EqualableViewIter = std::ranges::iterator_t<std::ranges::stride_view<EqualableView>>;
+static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
+static_assert(std::equality_comparable<EqualableViewIter>);
+
+// A stride_view iterator over a non-equality_comparable base should NOT be equality_comparable.
+using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
+using UnEqualableViewIter = std::ranges::iterator_t<std::ranges::stride_view<UnEqualableView>>;
+static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
+static_assert(!std::equality_comparable<UnEqualableViewIter>);
+
template <class Iter>
constexpr void testOne() {
using Range = BasicTestView<Iter, Iter>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
deleted file mode 100644
index 34404e056ef30..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-// constexpr __iterator& operator++()
-// constexpr void operator++(int)
-// constexpr __iterator operator++(int)
-// constexpr __iterator& operator--()
-// constexpr __iterator operator--(int)
-// constexpr __iterator& operator+=(difference_type __n)
-// constexpr __iterator& operator-=(difference_type __n)
-// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
-// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
-
-#include <functional>
-#include <iterator>
-#include <ranges>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "../types.h"
-#include "test_iterators.h"
-
-template <class T>
-concept CanPlus = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
-template <class T>
-concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
-
-template <class T>
-concept CanMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
- requires(T& t) { t - t; };
-
-template <class T>
-concept CanSentinelMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
- std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
- t - std::default_sentinel;
- std::default_sentinel - t;
- };
-
-template <class T>
-concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
- t + 1;
-} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
-template <class T>
-concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
-
-template <class T>
-concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
-template <class T>
-concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
-
-template <class T>
-concept CanSubscript = requires(T& t) { t[5]; };
-
-// What operators are valid for an iterator derived from a stride view
-// over an input view.(sized sentinel)
-using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
-using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPlus<StrideViewOverInputViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
-static_assert(!CanMinus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a forward view.(sized sentinel)
-using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
-using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a bidirectional view. (sized sentinel)
-using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
-using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
-
-static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::less_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a random access view. (non sized sentinel)
-using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
-using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
-
-static_assert(
- std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::less_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
-
-using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
-using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
-using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
-
-static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(!std::ranges::random_access_range<EqualableView>);
-static_assert(!std::three_way_comparable<EqualableView>);
-
-using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
-using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
-using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
-
-static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
-static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
-static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
-
-using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
-using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
-using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
-
-static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::ranges::random_access_range<UnEqualableView>);
-static_assert(!std::three_way_comparable<UnEqualableView>);
-
-template <typename Iter>
- requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
-constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) {
- using Base = BasicTestView<Iter, Iter>;
- // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
- // iterator and a default sentinel.
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::weakly_incrementable<StrideViewIterator>);
- static_assert(!std::ranges::forward_range<Base>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized input view.
-
- static_assert(!CanPostDecrement<StrideViewIterator>);
- static_assert(!CanPreDecrement<StrideViewIterator>);
- static_assert(!CanPlus<StrideViewIterator>);
- static_assert(!CanMinusEqual<StrideViewIterator>);
- static_assert(!CanDifferencePlus<StrideViewIterator>);
- static_assert(!CanDifferenceMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
- static_assert(!CanSubscript<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(zero_begin, end);
- auto base_view_offset_one = Base(one_begin, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
- auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
- auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- auto sv_one_offset_oneth_index = sv_one_offset_begin;
- auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
- // Check positive __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
- // Check negative __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
-
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- // assert((std::default_sentinel - )== 0);
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
-
- return true;
-}
-
-template <std::forward_iterator Iter, typename difference_type>
-constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) {
- // Test the forward-range operator- between two iterators (i.e., no ceil) and
- // an iterator and a default sentinel.
- using Base = BasicTestView<Iter, Iter>;
-
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::ranges::forward_range<Base>);
- static_assert(std::weakly_incrementable<StrideViewIterator>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized forward view (even though it is actually much more than that!).
-
- static_assert(CanMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(begin, end);
- auto base_view_offset_one = Base(begin + 1, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
- auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
- auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
-
- auto sv_one_offset_should_be_two = sv_one_offset_begin;
- auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
- static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
- assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
-
- // Check positive __n with non-exact multiple of left's stride.
- assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
- assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
-
- // Check negative __n with non-exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
-
- // Make sure that all sentinel operations work!
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
- return true;
-}
-
-constexpr bool test_properly_handling_missing() {
- // Check whether __missing_ gets handled properly.
- using Base = BasicTestView<int*, int*>;
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- auto base = Base(arr, arr + 10);
- auto strider = std::ranges::stride_view<Base>(base, 7);
-
- auto strider_iter = strider.end();
-
- strider_iter--;
- assert(*strider_iter == 8);
-
- // Now that we are back among the valid, we should
- // have a normal stride length back (i.e., __missing_
- // should be equal to 0).
- strider_iter--;
- assert(*strider_iter == 1);
-
- strider_iter++;
- assert(*strider_iter == 8);
-
- // By striding past the end, we are going to generate
- // another __missing_ != 0 value.
- strider_iter++;
- assert(strider_iter == strider.end());
-
- // Make sure that all sentinel operations work!
- assert(strider.end() == std::default_sentinel);
- assert(std::default_sentinel == strider.end());
-
- assert(strider_iter - std::default_sentinel == 0);
- assert(std::default_sentinel - strider.end() == 0);
- assert(std::default_sentinel - strider_iter == 0);
-
- // Let's make sure that the newly regenerated __missing__ gets used.
- strider_iter += -2;
- assert(*strider_iter == 1);
-
- return true;
-}
-
-int main(int, char**) {
- {
- constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- test_forward_operator_minus(arr, arr + 11, 4);
- test_forward_operator_minus(vec.begin(), vec.end(), 4);
- }
-
- {
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
- }
-
- test_properly_handling_missing();
- static_assert(test_properly_handling_missing());
- return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
index 93f4a511a94ab..2c3fbcc48554c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
@@ -6,14 +6,15 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr __iterator& operator++()
+// constexpr iterator& operator++()
// constexpr void operator++(int)
-// constexpr __iterator operator++(int)
+// constexpr iterator operator++(int)
+#include <cassert>
#include <iterator>
-#include <vector>
+#include <ranges>
#include "../types.h"
@@ -146,7 +147,7 @@ constexpr bool test_forward_operator_increment(Iter begin, Iter end) {
}
constexpr bool test_properly_handling_missing() {
- // Check whether __missing_ gets handled properly.
+ // Check whether the "missing" distance to the end gets handled properly.
using Base = BasicTestView<int*, int*>;
int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto base = Base(arr, arr + 10);
@@ -158,8 +159,8 @@ constexpr bool test_properly_handling_missing() {
assert(*strider_iter == 8);
// Now that we are back among the valid, we should
- // have a normal stride length back (i.e., __missing_
- // should be equal to 0).
+ // have a normal stride length back (i.e., there is no
+ // gap between the last stride and the end).
strider_iter--;
assert(*strider_iter == 1);
@@ -167,7 +168,7 @@ constexpr bool test_properly_handling_missing() {
assert(*strider_iter == 8);
// By striding past the end, we are going to generate
- // another __missing_ != 0 value.
+ // another gap between the last stride and the end.
strider_iter++;
assert(strider_iter == strider.end());
@@ -179,27 +180,31 @@ constexpr bool test_properly_handling_missing() {
assert(std::default_sentinel - strider.end() == 0);
assert(std::default_sentinel - strider_iter == 0);
- // Let's make sure that the newly regenerated __missing__ gets used.
+ // Let's make sure that the newly regenerated gap gets used.
strider_iter += -2;
assert(*strider_iter == 1);
return true;
}
-int main(int, char**) {
+constexpr bool test() {
{
- constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
test_forward_operator_increment(arr, arr + 11);
- test_forward_operator_increment(vec.begin(), vec.end());
}
+ test_properly_handling_missing();
+ return true;
+}
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ // Non-forward iterators can't be tested in a constexpr context.
{
int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
test_non_forward_operator_increment(SizedInputIter(arr), SizedInputIter(arr + 3), SizedInputIter(arr + 10));
}
- test_properly_handling_missing();
- static_assert(test_properly_handling_missing());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
index 3f355b5217dfa..adacd6f7eb0d1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
@@ -6,11 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// friend constexpr range_rvalue_reference_t<_Base> iter_move(__iterator const& __it)
-// noexcept(noexcept(ranges::iter_move(__it.__current_)))
+// friend constexpr range_rvalue_reference_t<Base> iter_move(const iterator& i)
+// noexcept(noexcept(ranges::iter_move(i.current_)))
+#include <cassert>
#include <ranges>
#include <vector>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
index 75eccbfedac86..493bff0671c0c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
@@ -6,12 +6,13 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// friend constexpr void iter_swap(__iterator const& __x, __iterator const& __y)
-// noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
-// requires indirectly_swappable<iterator_t<_Base>>
+// friend constexpr void iter_swap(const iterator& x, const iterator& y)
+// noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
+// requires indirectly_swappable<iterator_t<Base>>
+#include <cassert>
#include <ranges>
#include "../types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
deleted file mode 100644
index 5a35ecc066c43..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-// constexpr __iterator& operator++()
-// constexpr void operator++(int)
-// constexpr __iterator operator++(int)
-// constexpr __iterator& operator--()
-// constexpr __iterator operator--(int)
-// constexpr __iterator& operator+=(difference_type __n)
-// constexpr __iterator& operator-=(difference_type __n)
-// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
-// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
-
-#include <functional>
-#include <iterator>
-#include <ranges>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "../types.h"
-#include "test_iterators.h"
-
-template <class T>
-concept CanPlusEqual = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
-template <class T>
-concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
-
-template <class T>
-concept CanMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
- requires(T& t) { t - t; };
-
-template <class T>
-concept CanSentinelMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
- std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
- t - std::default_sentinel;
- std::default_sentinel - t;
- };
-
-template <class T>
-concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
- t + 1;
-} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
-template <class T>
-concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
-
-template <class T>
-concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
-template <class T>
-concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
-
-template <class T>
-concept CanSubscript = requires(T& t) { t[5]; };
-
-// What operators are valid for an iterator derived from a stride view
-// over an input view.(sized sentinel)
-using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
-using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPlusEqual<StrideViewOverInputViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
-static_assert(!CanMinus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a forward view.(sized sentinel)
-using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
-using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPlusEqual<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a bidirectional view. (sized sentinel)
-using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
-using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
-
-static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanPlusEqual<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::less_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a random access view. (non sized sentinel)
-using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
-using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPlusEqual<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
-
-static_assert(
- std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::less_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
-
-using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
-using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
-using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
-
-static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(!std::ranges::random_access_range<EqualableView>);
-static_assert(!std::three_way_comparable<EqualableView>);
-
-using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
-using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
-using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
-
-static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
-static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
-static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
-
-using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
-using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
-using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
-
-static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::ranges::random_access_range<UnEqualableView>);
-static_assert(!std::three_way_comparable<UnEqualableView>);
-
-template <typename Iter>
- requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
-constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) {
- using Base = BasicTestView<Iter, Iter>;
- // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
- // iterator and a default sentinel.
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::weakly_incrementable<StrideViewIterator>);
- static_assert(!std::ranges::forward_range<Base>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized input view.
-
- static_assert(!CanPostDecrement<StrideViewIterator>);
- static_assert(!CanPreDecrement<StrideViewIterator>);
- static_assert(!CanPlusEqual<StrideViewIterator>);
- static_assert(!CanMinusEqual<StrideViewIterator>);
- static_assert(!CanDifferencePlus<StrideViewIterator>);
- static_assert(!CanDifferenceMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
- static_assert(!CanSubscript<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(zero_begin, end);
- auto base_view_offset_one = Base(one_begin, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
- auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
- auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- auto sv_one_offset_oneth_index = sv_one_offset_begin;
- auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
- // Check positive __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
- // Check negative __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
-
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- // assert((std::default_sentinel - )== 0);
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
-
- return true;
-}
-
-template <std::forward_iterator Iter, typename difference_type>
-constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) {
- // Test the forward-range operator- between two iterators (i.e., no ceil) and
- // an iterator and a default sentinel.
- using Base = BasicTestView<Iter, Iter>;
-
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::ranges::forward_range<Base>);
- static_assert(std::weakly_incrementable<StrideViewIterator>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized forward view (even though it is actually much more than that!).
-
- static_assert(CanMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(begin, end);
- auto base_view_offset_one = Base(begin + 1, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
- auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
- auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
-
- auto sv_one_offset_should_be_two = sv_one_offset_begin;
- auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
- static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
- assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
-
- // Check positive __n with non-exact multiple of left's stride.
- assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
- assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
-
- // Check negative __n with non-exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
-
- // Make sure that all sentinel operations work!
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
- return true;
-}
-
-constexpr bool test_properly_handling_missing() {
- // Check whether __missing_ gets handled properly.
- using Base = BasicTestView<int*, int*>;
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- auto base = Base(arr, arr + 10);
- auto strider = std::ranges::stride_view<Base>(base, 7);
-
- auto strider_iter = strider.end();
-
- strider_iter--;
- assert(*strider_iter == 8);
-
- // Now that we are back among the valid, we should
- // have a normal stride length back (i.e., __missing_
- // should be equal to 0).
- strider_iter--;
- assert(*strider_iter == 1);
-
- strider_iter++;
- assert(*strider_iter == 8);
-
- // By striding past the end, we are going to generate
- // another __missing_ != 0 value.
- strider_iter++;
- assert(strider_iter == strider.end());
-
- // Make sure that all sentinel operations work!
- assert(strider.end() == std::default_sentinel);
- assert(std::default_sentinel == strider.end());
-
- assert(strider_iter - std::default_sentinel == 0);
- assert(std::default_sentinel - strider.end() == 0);
- assert(std::default_sentinel - strider_iter == 0);
-
- // Let's make sure that the newly regenerated __missing__ gets used.
- strider_iter += -2;
- assert(*strider_iter == 1);
-
- return true;
-}
-
-int main(int, char**) {
- {
- constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- test_forward_operator_minus(arr, arr + 11, 4);
- test_forward_operator_minus(vec.begin(), vec.end(), 4);
- }
-
- {
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
- }
-
- test_properly_handling_missing();
- static_assert(test_properly_handling_missing());
- return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
new file mode 100644
index 0000000000000..9664fac7172b2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
@@ -0,0 +1,186 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+// friend constexpr difference_type operator-(default_sentinel_t, const iterator& x)
+// friend constexpr difference_type operator-(const iterator& x, default_sentinel_t)
+// friend constexpr iterator operator-(const iterator& i, difference_type s)
+
+#include <cassert>
+#include <ranges>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanMinus =
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
+ requires(T& t) { t - t; };
+
+template <class T>
+concept CanSentinelMinus =
+ std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
+ std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
+ t - std::default_sentinel;
+ std::default_sentinel - t;
+ };
+
+template <class T>
+concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
+
+// Input view: has sentinel minus but not iter-iter minus or difference minus.
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+static_assert(!CanMinus<StrideViewOverInputViewIter>);
+static_assert(!CanDifferenceMinus<StrideViewOverInputViewIter>);
+static_assert(CanSentinelMinus<StrideViewOverInputViewIter>);
+
+// Forward view: has sentinel minus but not iter-iter minus or difference minus.
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIter = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+static_assert(!CanMinus<StrideViewOverForwardViewIter>);
+static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIter>);
+static_assert(CanSentinelMinus<StrideViewOverForwardViewIter>);
+
+// Bidirectional view: has sentinel minus but not iter-iter minus or difference minus.
+using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirViewIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
+static_assert(!CanMinus<StrideViewOverBidirViewIter>);
+static_assert(!CanDifferenceMinus<StrideViewOverBidirViewIter>);
+static_assert(CanSentinelMinus<StrideViewOverBidirViewIter>);
+
+// Random access view: has iter-iter minus and difference minus, but no sentinel minus (non-sized sentinel).
+using RAView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRAViewIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
+static_assert(CanMinus<StrideViewOverRAViewIter>);
+static_assert(CanDifferenceMinus<StrideViewOverRAViewIter>);
+static_assert(!CanSentinelMinus<StrideViewOverRAViewIter>);
+
+template <typename Iter>
+ requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
+constexpr bool test_non_forward_minus(Iter zero_begin, Iter one_begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+
+ auto base_zero = Base(zero_begin, end);
+ auto base_one = Base(one_begin, end);
+ auto sv_zero = std::ranges::stride_view(base_zero, 3);
+ auto sv_one = std::ranges::stride_view(base_one, 3);
+
+ auto begin0 = sv_zero.begin();
+ auto mid0 = begin0;
+ ++mid0; // stride 3 -> index 3
+ auto far0 = mid0;
+ ++far0; // stride 3 -> index 6
+
+ auto begin1 = sv_one.begin();
+ auto mid1 = begin1;
+ ++mid1; // stride 3 -> index 4
+
+ // Positive differences (uses ceil for non-forward).
+ assert(mid0 - begin0 == 1);
+ assert(far0 - begin0 == 2);
+ assert(begin1 - begin0 == 1);
+ assert(mid1 - begin0 == 2);
+
+ // Negative differences.
+ assert(begin0 - mid0 == -1);
+ assert(begin0 - far0 == -2);
+ assert(begin0 - begin1 == -1);
+ assert(begin0 - mid1 == -2);
+
+ // Sentinel minus.
+ assert(std::default_sentinel - sv_zero.begin() == std::ranges::distance(sv_zero));
+ assert(sv_zero.begin() - std::default_sentinel == -std::ranges::distance(sv_zero));
+ assert(std::default_sentinel - sv_zero.end() == 0);
+ assert(sv_zero.end() - std::default_sentinel == 0);
+
+ return true;
+}
+
+template <std::forward_iterator Iter>
+constexpr bool test_forward_minus(Iter begin, Iter end) {
+ using Base = BasicTestView<Iter, Iter>;
+
+ auto base_zero = Base(begin, end);
+ auto base_one = Base(begin + 1, end);
+ auto sv_zero = std::ranges::stride_view(base_zero, 3);
+ auto sv_one = std::ranges::stride_view(base_one, 3);
+
+ auto begin0 = sv_zero.begin();
+ auto mid0 = begin0;
+ ++mid0; // stride 3 -> value 4
+ auto far0 = mid0;
+ ++far0; // stride 3 -> value 7
+
+ auto begin1 = sv_one.begin(); // value 2
+ auto mid1 = begin1;
+ ++mid1; // value 5
+
+ // Forward range uses exact division (no ceil).
+ assert(mid0 - begin0 == 1);
+ assert(far0 - begin0 == 2);
+ assert(begin1 - begin0 == 0);
+ assert(mid1 - begin0 == 1);
+
+ assert(begin0 - mid0 == -1);
+ assert(begin0 - far0 == -2);
+
+ // Sentinel minus.
+ assert(std::default_sentinel - sv_zero.begin() == std::ranges::distance(sv_zero));
+ assert(sv_zero.begin() - std::default_sentinel == -std::ranges::distance(sv_zero));
+
+ return true;
+}
+
+constexpr bool test_difference_minus() {
+ // operator-(iterator, difference_type) — only for random access ranges.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+
+ auto base = Base(arr, arr + 10);
+ auto sv = std::ranges::stride_view(base, 3);
+
+ auto it = sv.begin();
+ ++it;
+ ++it;
+ ++it; // at index 9
+
+ auto it2 = it - 2; // back to index 3
+ assert(*it2 == 4);
+ auto it3 = it - 3; // back to index 0
+ assert(*it3 == 1);
+
+ return true;
+}
+
+constexpr bool test() {
+ {
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_minus(arr, arr + 11);
+
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ test_forward_minus(vec.begin(), vec.end());
+ }
+ test_difference_minus();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ {
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ test_non_forward_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp
new file mode 100644
index 0000000000000..7629c4df8e4ec
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr iterator& operator-=(difference_type n)
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
+
+// Input view: no -=.
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideViewOverInputViewIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+static_assert(!CanMinusEqual<StrideViewOverInputViewIter>);
+
+// Forward view: no -=.
+using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideViewOverForwardViewIter = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
+static_assert(!CanMinusEqual<StrideViewOverForwardViewIter>);
+
+// Bidirectional view: no -=.
+using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideViewOverBidirViewIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
+static_assert(!CanMinusEqual<StrideViewOverBidirViewIter>);
+
+// Random access view: has -=.
+using RAView = BasicTestView<random_access_iterator<int*>>;
+using StrideViewOverRAViewIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
+static_assert(CanMinusEqual<StrideViewOverRAViewIter>);
+
+constexpr bool test() {
+ {
+ // Basic -= test with stride 1.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 10), 1);
+ auto it = sv.begin();
+ it += 5;
+ assert(*it == 6);
+ it -= 3;
+ assert(*it == 3);
+ it -= 2;
+ assert(*it == 1);
+ }
+ {
+ // -= test with stride 3.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3);
+ auto it = sv.begin();
+ it += 3; // at index 9 (value 10)
+ assert(*it == 10);
+ it -= 1; // at index 6 (value 7)
+ assert(*it == 7);
+ it -= 2; // at index 0 (value 1)
+ assert(*it == 1);
+ }
+ {
+ // -= when the stride doesn't evenly divide the range: stride past the end, then back.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view<Base>(Base(arr, arr + 10), 7);
+ auto it = sv.end();
+ it -= 1; // back to last strided element (value 8)
+ assert(*it == 8);
+ it -= 1; // back to first element (value 1)
+ assert(*it == 1);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
deleted file mode 100644
index f8b077770df51..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-// constexpr __iterator& operator++()
-// constexpr void operator++(int)
-// constexpr __iterator operator++(int)
-// constexpr __iterator& operator--()
-// constexpr __iterator operator--(int)
-// constexpr __iterator& operator+=(difference_type __n)
-// constexpr __iterator& operator-=(difference_type __n)
-// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t)
-// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
-// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
-
-#include <functional>
-#include <iterator>
-#include <ranges>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "../types.h"
-#include "test_iterators.h"
-
-template <class T>
-concept CanPlus = std::is_same_v<T&, decltype(std::declval<T>() += 1)> && requires(T& t) { t += 1; };
-template <class T>
-concept CanMinusEqual = std::is_same_v<T&, decltype(std::declval<T>() -= 1)> && requires(T& t) { t -= 1; };
-
-template <class T>
-concept CanMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::declval<T>())> &&
- requires(T& t) { t - t; };
-
-template <class T>
-concept CanSentinelMinus =
- // Note: Do *not* use std::iterator_traits here because T may not have
- // all the required pieces when it is not a forward_range.
- std::is_same_v<typename T::difference_type, decltype(std::declval<T>() - std::default_sentinel)> &&
- std::is_same_v<typename T::difference_type, decltype(std::default_sentinel - std::declval<T>())> && requires(T& t) {
- t - std::default_sentinel;
- std::default_sentinel - t;
- };
-
-template <class T>
-concept CanDifferencePlus = std::is_same_v<T, decltype(std::declval<T>() + 1)> && requires(T& t) {
- t + 1;
-} && std::is_same_v<T, decltype(1 + std::declval<T>())> && requires(T& t) { 1 + t; };
-template <class T>
-concept CanDifferenceMinus = std::is_same_v<T, decltype(std::declval<T>() - 1)> && requires(T& t) { t - 1; };
-
-template <class T>
-concept CanPostDecrement = std::is_same_v<T, decltype(std::declval<T>()--)> && requires(T& t) { t--; };
-template <class T>
-concept CanPreDecrement = std::is_same_v<T&, decltype(--std::declval<T>())> && requires(T& t) { --t; };
-
-template <class T>
-concept CanSubscript = requires(T& t) { t[5]; };
-
-// What operators are valid for an iterator derived from a stride view
-// over an input view.(sized sentinel)
-using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
-using StrideViewOverInputViewIterator = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverInputViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverInputViewIterator>);
-static_assert(!CanPlus<StrideViewOverInputViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverInputViewIterator>);
-static_assert(!CanMinus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverInputViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverInputViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverInputViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverInputViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverInputViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a forward view.(sized sentinel)
-using ForwardView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
-using StrideViewOverForwardViewIterator = std::ranges::iterator_t<std::ranges::stride_view<ForwardView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverForwardViewIterator>);
-
-static_assert(!CanPostDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPreDecrement<StrideViewOverForwardViewIterator>);
-static_assert(!CanPlus<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverForwardViewIterator>);
-static_assert(!CanMinus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverForwardViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverForwardViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverForwardViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverForwardViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::less_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-static_assert(
- !std::is_invocable_v<std::greater_equal<>, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverForwardViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a bidirectional view. (sized sentinel)
-using BidirectionalView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
-using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t<std::ranges::stride_view<BidirectionalView>>;
-
-static_assert(CanPostDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanPlus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinusEqual<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferencePlus<StrideViewOverBidirectionalViewIterator>);
-static_assert(!CanDifferenceMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(CanSentinelMinus<StrideViewOverBidirectionalViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!std::is_invocable_v<std::less<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::less_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-static_assert(!std::is_invocable_v<std::greater_equal<>,
- StrideViewOverBidirectionalViewIterator,
- StrideViewOverBidirectionalViewIterator>);
-
-static_assert(!CanSubscript<StrideViewOverBidirectionalViewIterator>);
-
-// What operators are valid for an iterator derived from a stride view
-// over a random access view. (non sized sentinel)
-using RandomAccessView = BasicTestView<random_access_iterator<int*>>;
-using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::ranges::stride_view<RandomAccessView>>;
-
-static_assert(std::weakly_incrementable<StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanPostDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPreDecrement<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinusEqual<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferencePlus<StrideViewOverRandomAccessViewIterator>);
-static_assert(CanDifferenceMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(!CanSentinelMinus<StrideViewOverRandomAccessViewIterator>);
-static_assert(
- std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::invocable<std::equal_to<>, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>);
-static_assert(std::invocable<std::equal_to<>, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>);
-
-static_assert(
- std::is_invocable_v<std::less<>, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::less_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-static_assert(std::is_invocable_v<std::greater_equal<>,
- StrideViewOverRandomAccessViewIterator,
- StrideViewOverRandomAccessViewIterator>);
-
-static_assert(CanSubscript<StrideViewOverRandomAccessViewIterator>);
-
-using EqualableView = BasicTestView<cpp17_input_iterator<int*>>;
-using EqualableViewStrideView = std::ranges::stride_view<EqualableView>;
-using EqualableViewStrideViewIter = std::ranges::iterator_t<EqualableViewStrideView>;
-
-static_assert(std::equality_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(std::equality_comparable<EqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<EqualableView>>);
-static_assert(!std::ranges::random_access_range<EqualableView>);
-static_assert(!std::three_way_comparable<EqualableView>);
-
-using ThreeWayComparableView = BasicTestView<three_way_contiguous_iterator<int*>>;
-using ThreeWayComparableViewStrideView = std::ranges::stride_view<ThreeWayComparableView>;
-using ThreeWayComparableStrideViewIter = std::ranges::iterator_t<ThreeWayComparableViewStrideView>;
-
-static_assert(std::three_way_comparable<std::ranges::iterator_t<ThreeWayComparableView>>);
-static_assert(std::ranges::random_access_range<ThreeWayComparableView>);
-static_assert(std::three_way_comparable<ThreeWayComparableStrideViewIter>);
-
-using UnEqualableView = ViewOverNonCopyableIterator<cpp20_input_iterator<int*>>;
-using UnEqualableViewStrideView = std::ranges::stride_view<UnEqualableView>;
-using UnEqualableViewStrideViewIter = std::ranges::iterator_t<UnEqualableViewStrideView>;
-
-static_assert(!std::equality_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::equality_comparable<UnEqualableViewStrideViewIter>);
-
-static_assert(!std::three_way_comparable<std::ranges::iterator_t<UnEqualableView>>);
-static_assert(!std::ranges::random_access_range<UnEqualableView>);
-static_assert(!std::three_way_comparable<UnEqualableView>);
-
-template <typename Iter>
- requires std::sized_sentinel_for<Iter, Iter> && (!std::forward_iterator<Iter>)
-constexpr bool test_non_forward_operator_plus(Iter zero_begin, Iter one_begin, Iter end) {
- using Base = BasicTestView<Iter, Iter>;
- // Test the non-forward-range operator- between two iterators (i.e., ceil) and an
- // iterator and a default sentinel.
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::weakly_incrementable<StrideViewIterator>);
- static_assert(!std::ranges::forward_range<Base>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized input view.
-
- static_assert(!CanPostDecrement<StrideViewIterator>);
- static_assert(!CanPreDecrement<StrideViewIterator>);
- static_assert(!CanPlus<StrideViewIterator>);
- static_assert(!CanMinusEqual<StrideViewIterator>);
- static_assert(!CanDifferencePlus<StrideViewIterator>);
- static_assert(!CanDifferenceMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- static_assert(!std::is_invocable_v<std::less<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::less_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater<>, StrideViewIterator, StrideViewIterator>);
- static_assert(!std::is_invocable_v<std::greater_equal<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, std::default_sentinel_t, StrideViewIterator>);
- static_assert(std::is_invocable_v<std::equal_to<>, StrideViewIterator, std::default_sentinel_t>);
- static_assert(!CanSubscript<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(zero_begin, end);
- auto base_view_offset_one = Base(one_begin, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_zeroth_index = sv_zero_offset_begin;
- auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
- auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- auto sv_one_offset_oneth_index = sv_one_offset_begin;
- auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes.
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2);
- // Check positive __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1);
- assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2);
- // Check negative __n with non-exact multiple of left's stride (will do ceil here).
- assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1);
- assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2);
-
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0);
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- // assert((std::default_sentinel - )== 0);
-
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
-
- return true;
-}
-
-template <std::forward_iterator Iter, typename difference_type>
-constexpr bool test_forward_operator_plus(Iter begin, Iter end, difference_type distance) {
- // Test the forward-range operator- between two iterators (i.e., no ceil) and
- // an iterator and a default sentinel.
- using Base = BasicTestView<Iter, Iter>;
-
- using StrideViewIterator = std::ranges::iterator_t<std::ranges::stride_view<Base>>;
- static_assert(std::ranges::forward_range<Base>);
- static_assert(std::weakly_incrementable<StrideViewIterator>);
-
- // First, what operators are valid for an iterator derived from a stride view
- // over a sized forward view (even though it is actually much more than that!).
-
- static_assert(CanMinus<StrideViewIterator>);
- static_assert(CanSentinelMinus<StrideViewIterator>);
-
- auto base_view_offset_zero = Base(begin, end);
- auto base_view_offset_one = Base(begin + 1, end);
- auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3);
- auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3);
-
- auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin();
- auto sv_one_offset_begin = stride_view_over_base_one_offset.begin();
-
- auto sv_zero_offset_should_be_one = sv_zero_offset_begin;
- auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin;
- auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin;
-
- auto sv_one_offset_should_be_two = sv_one_offset_begin;
- auto sv_one_offset_should_be_five = ++sv_one_offset_begin;
-
- static_assert(std::sized_sentinel_for<std::ranges::iterator_t<Base>, std::ranges::iterator_t<Base>>);
- static_assert(CanMinus<decltype(sv_zero_offset_begin)>);
- static_assert(std::forward_iterator<std::ranges::iterator_t<Base>>);
-
- // Check positive __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1);
- assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2);
-
- // Check positive __n with non-exact multiple of left's stride.
- assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0);
- assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1);
-
- // Check negative __n with exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1);
- assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2);
-
- // Check negative __n with non-exact multiple of left's stride.
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0);
- assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1);
-
- // Make sure that all sentinel operations work!
- assert(stride_view_over_base_zero_offset.end() == std::default_sentinel);
- assert(std::default_sentinel == stride_view_over_base_zero_offset.end());
-
- assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() ==
- std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel ==
- -std::ranges::distance(stride_view_over_base_zero_offset));
- assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance);
- assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance);
- return true;
-}
-
-constexpr bool test_properly_handling_missing() {
- // Check whether __missing_ gets handled properly.
- using Base = BasicTestView<int*, int*>;
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- auto base = Base(arr, arr + 10);
- auto strider = std::ranges::stride_view<Base>(base, 7);
-
- auto strider_iter = strider.end();
-
- strider_iter--;
- assert(*strider_iter == 8);
-
- // Now that we are back among the valid, we should
- // have a normal stride length back (i.e., __missing_
- // should be equal to 0).
- strider_iter--;
- assert(*strider_iter == 1);
-
- strider_iter++;
- assert(*strider_iter == 8);
-
- // By striding past the end, we are going to generate
- // another __missing_ != 0 value.
- strider_iter++;
- assert(strider_iter == strider.end());
-
- // Make sure that all sentinel operations work!
- assert(strider.end() == std::default_sentinel);
- assert(std::default_sentinel == strider.end());
-
- assert(strider_iter - std::default_sentinel == 0);
- assert(std::default_sentinel - strider.end() == 0);
- assert(std::default_sentinel - strider_iter == 0);
-
- // Let's make sure that the newly regenerated __missing__ gets used.
- strider_iter += -2;
- assert(*strider_iter == 1);
-
- return true;
-}
-
-int main(int, char**) {
- {
- constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- test_forward_operator_plus(arr, arr + 11, 4);
- test_forward_operator_plus(vec.begin(), vec.end(), 4);
- }
-
- {
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- test_non_forward_operator_plus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
- }
-
- test_properly_handling_missing();
- static_assert(test_properly_handling_missing());
- return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
index 59d5473fa3215..5018bd0ebb8a0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
@@ -6,11 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr __iterator operator+(difference_type __n, const __iterator &__i)
-// constexpr __iterator operator+(const __iterator &__i, difference_type __n)
+// constexpr iterator operator+(difference_type n, const iterator& i)
+// constexpr iterator operator+(const iterator& i, difference_type n)
+#include <cassert>
#include <ranges>
#include <vector>
@@ -19,10 +20,10 @@
template <class T>
concept CanPlus =
- std::is_same_v<T, decltype(std::declval<T>() + std::declval<typename T::__iterator::difference_type>())> &&
- std::is_same_v<T, decltype(std::declval<typename T::__iterator::difference_type>() + std::declval<T>())> &&
- requires(T& t, T::__iterator::difference_type& u) { t = t + u; } &&
- requires(T& t, T::__iterator::difference_type& u) { t = u + t; };
+ std::is_same_v<T, decltype(std::declval<T>() + std::declval<typename T::difference_type>())> &&
+ std::is_same_v<T, decltype(std::declval<typename T::difference_type>() + std::declval<T>())> &&
+ requires(T& t, typename T::difference_type u) { t = t + u; } &&
+ requires(T& t, typename T::difference_type u) { t = u + t; };
// Make sure that we cannot use + on a stride view iterator and difference_type
// over an input view.(sized sentinel)
@@ -57,20 +58,16 @@ using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::rang
static_assert(std::ranges::random_access_range<RandomAccessView<>>);
static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
-constexpr bool test_random_access_operator_plus_equal() {
- using Iter = std::vector<int>::iterator;
- using Diff = Iter::difference_type;
+constexpr bool test() {
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
// Test the operator+ between an iterator and its difference type. Pay attention solely to
// stride views over random-access ranges because operator+ is not applicable to others.
auto begin = vec.begin();
auto end = vec.end();
- Diff distance = 4;
+ auto distance = 4;
- // Do not use the RandomAccessView defined in types.h to give the test user more power
- // to customize an iterator and a default sentinel.
- using Base = RandomAccessView<Iter>;
+ using Base = RandomAccessView<std::vector<int>::iterator>;
static_assert(std::ranges::random_access_range<Base>);
auto base_view = Base(begin, end);
@@ -91,13 +88,8 @@ constexpr bool test_random_access_operator_plus_equal() {
return true;
}
-consteval bool do_static_tests() {
- assert(test_random_access_operator_plus_equal());
- return true;
-}
-
int main(int, char**) {
- static_assert(do_static_tests());
- assert(do_static_tests());
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
index 01582e22066c6..fe302fee565d7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr __iterator& operator+=(difference_type __n)
+// constexpr iterator& operator+=(difference_type n)
+#include <cassert>
#include <ranges>
-#include <utility>
#include <vector>
#include "../types.h"
@@ -19,8 +19,8 @@
template <class T>
concept CanPlus =
- std::is_same_v<T&, decltype(std::declval<T>() += std::declval<typename T::__iterator::difference_type>())> &&
- requires(T& t, T::__iterator::difference_type& u) { t += u; };
+ std::is_same_v<T&, decltype(std::declval<T>() += std::declval<typename T::difference_type>())> &&
+ requires(T& t, typename T::difference_type u) { t += u; };
// Make sure that we cannot use += on a stride view iterator
// over an input view.(sized sentinel)
@@ -55,20 +55,16 @@ using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t<std::rang
static_assert(std::ranges::random_access_range<RandomAccessView<>>);
static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
-constexpr bool test_random_access_operator_plus_equal() {
+constexpr bool test() {
+ std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+
using Iter = std::vector<int>::iterator;
using Diff = Iter::difference_type;
- std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- // Test the operator+ between an iterator and its difference type. Pay attention solely to
- // stride views over random-access ranges because operator+ is not applicable to others.
auto begin = vec.begin();
auto end = vec.end();
Diff distance = 4;
- // Test the forward-range operator+= between an iterator and its difference type.
- // Do not use the RandomAccessView defined in types.h to give the test user more power
- // to customize an iterator and a default sentinel.
using Base = RandomAccessView<Iter>;
static_assert(std::ranges::random_access_range<Base>);
@@ -89,21 +85,16 @@ constexpr bool test_random_access_operator_plus_equal() {
auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, big_step);
sv_bv_begin = stride_view_over_base_view_big_step.begin();
- // This += should move us into a position where the __missing_ will come into play.
- // Do a -= 1 here to confirm that the __missing_ is taken into account.
+ // This += should move us into a position where the stride doesn't evenly divide the range.
+ // Do a -= 1 here to confirm that the remainder is taken into account.
sv_bv_begin += 2;
sv_bv_begin -= 1;
assert(*sv_bv_begin == *(stride_view_over_base_view.begin() + big_step));
return true;
}
-consteval bool do_static_tests() {
- assert(test_random_access_operator_plus_equal());
- return true;
-}
-
int main(int, char**) {
- static_assert(do_static_tests());
- assert(do_static_tests());
+ test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
new file mode 100644
index 0000000000000..3cdbbe882c5e4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr decltype(auto) operator[](difference_type n) const
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanSubscript = requires(T& t) { t[0]; };
+
+// Input view: no subscript.
+using InputView = BasicTestView<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>;
+using StrideInputIter = std::ranges::iterator_t<std::ranges::stride_view<InputView>>;
+static_assert(!CanSubscript<StrideInputIter>);
+
+// Forward view: no subscript.
+using FwdView = BasicTestView<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>;
+using StrideFwdIter = std::ranges::iterator_t<std::ranges::stride_view<FwdView>>;
+static_assert(!CanSubscript<StrideFwdIter>);
+
+// Bidirectional view: no subscript.
+using BidirView = BasicTestView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+using StrideBidirIter = std::ranges::iterator_t<std::ranges::stride_view<BidirView>>;
+static_assert(!CanSubscript<StrideBidirIter>);
+
+// Random access view: subscript available.
+using RAView = BasicTestView<random_access_iterator<int*>>;
+using StrideRAIter = std::ranges::iterator_t<std::ranges::stride_view<RAView>>;
+static_assert(CanSubscript<StrideRAIter>);
+
+constexpr bool test() {
+ {
+ // Subscript with stride 1.
+ int arr[] = {10, 20, 30, 40, 50};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 5), 1);
+ auto it = sv.begin();
+
+ assert(it[0] == 10);
+ assert(it[1] == 20);
+ assert(it[2] == 30);
+ assert(it[3] == 40);
+ assert(it[4] == 50);
+ }
+ {
+ // Subscript with stride 2.
+ int arr[] = {10, 20, 30, 40, 50};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2);
+ auto it = sv.begin();
+
+ assert(it[0] == 10);
+ assert(it[1] == 30);
+ assert(it[2] == 50);
+ }
+ {
+ // Subscript with stride 3.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3);
+ auto it = sv.begin();
+
+ assert(it[0] == 1);
+ assert(it[1] == 4);
+ assert(it[2] == 7);
+ assert(it[3] == 10);
+ }
+ {
+ // Subscript from a non-begin position.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ auto sv = std::ranges::stride_view(Base(arr, arr + 10), 3);
+ auto it = sv.begin();
+ ++it; // now at index 3 (value 4)
+
+ assert(it[0] == 4);
+ assert(it[1] == 7);
+ assert(it[2] == 10);
+ assert(it[-1] == 1);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
index 4a90ee750bf7d..28159cc153c1b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -6,9 +6,10 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// constexpr auto size()
+// constexpr auto size() requires sized_range<V>
+// constexpr auto size() const requires sized_range<const V>
#include <ranges>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
index 822f4f360f263..9266e6840fccf 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
@@ -6,12 +6,14 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++23
-// constexpr range_difference_t<_View> stride() const noexcept;
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// constexpr range_difference_t<V> stride() const noexcept;
+#include <cassert>
#include <ranges>
#include "test_iterators.h"
+#include "test_macros.h"
#include "types.h"
constexpr bool test() {
>From 2d06dfbb103a981ed377943d10da85cdac4ab328 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 20 Mar 2026 15:44:29 -0400
Subject: [PATCH 9/9] Apply more changes requested by Konstantin, and drive-by
fixes
---
.../concept.compile.pass.cpp | 4 +-
.../range.stride.view/ctad.pass.cpp | 16 +++++--
.../range.stride.view/ctor.pass.cpp | 39 ++++++++-------
.../iterator/ctor.copy.pass.cpp | 47 +++++++------------
.../iterator/ctor.default.pass.cpp | 8 +---
.../iterator/decrement.pass.cpp | 3 +-
.../range.stride.view/iterator/equal.pass.cpp | 2 +-
.../iterator/increment.pass.cpp | 8 ++--
.../iterator/iter_move.pass.cpp | 9 ++--
.../iterator/iter_swap.pass.cpp | 17 ++++---
.../range.stride.view/iterator/plus.pass.cpp | 38 ++++++++-------
.../iterator/plus_equal.pass.cpp | 2 +
.../range.stride.view/size.pass.cpp | 25 +++++-----
.../range.stride.view/stride.pass.cpp | 18 +++----
14 files changed, 121 insertions(+), 115 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
index 652dfcff3b0c3..0fe52ed9c3453 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
@@ -21,10 +21,10 @@ concept CanStrideView = requires { std::ranges::stride_view<I>{I{}, D}; };
// Ensure that the InputRangeNotIndirectlyReadable is a valid range.
static_assert(std::ranges::range<InputRangeNotIndirectlyReadable>);
-// Ensure that the InputRangeNotIndirectlyReadable's is not an input range ...
+// Ensure that the InputRangeNotIndirectlyReadable's iterator is not an input range ...
static_assert(!std::ranges::input_range<std::ranges::iterator_t<InputRangeNotIndirectlyReadable>>);
// Because CanStrideView requires that the range/view type be default constructible, let's double check that ...
-static_assert(std::is_constructible_v<InputRangeNotIndirectlyReadable>);
+static_assert(std::is_default_constructible_v<InputRangeNotIndirectlyReadable>);
// And now, finally, let's make sure that we cannot stride over a range whose iterator is not an input iterator ...
static_assert(!CanStrideView<InputRangeNotIndirectlyReadable, 1>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
index de193bed51da1..0a3e1fa8af095 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
@@ -39,26 +39,31 @@ constexpr bool test() {
auto base_range = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
auto base_range_for_move = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
+ // Deduction from lvalue view and rvalue view.
auto copied_stride_base_view = std::ranges::stride_view(base_view, 2);
auto moved_stride_base_view = std::ranges::stride_view(std::move(base_view_for_move), 2);
+ // Deduction from lvalue range (-> ref_view) and rvalue range (-> owning_view).
auto copied_stride_base_range = std::ranges::stride_view(base_range, 2);
auto moved_stride_base_range = std::ranges::stride_view(std::move(base_range_for_move), 2);
- static_assert(std::same_as< decltype(copied_stride_base_view), std::ranges::stride_view<BaseView>>);
- static_assert(std::same_as< decltype(moved_stride_base_view), std::ranges::stride_view<BaseView>>);
+ // Verify deduced types for views.
+ static_assert(std::same_as<decltype(copied_stride_base_view), std::ranges::stride_view<BaseView>>);
+ static_assert(std::same_as<decltype(moved_stride_base_view), std::ranges::stride_view<BaseView>>);
+ // Verify deduced types for ranges: lvalue -> ref_view, rvalue -> owning_view.
static_assert(
- std::same_as< decltype(copied_stride_base_range), std::ranges::stride_view<std::ranges::ref_view<BaseRange>> >);
+ std::same_as<decltype(copied_stride_base_range), std::ranges::stride_view<std::ranges::ref_view<BaseRange>> >);
static_assert(
- std::same_as< decltype(moved_stride_base_range), std::ranges::stride_view<std::ranges::owning_view<BaseRange>> >);
+ std::same_as<decltype(moved_stride_base_range), std::ranges::stride_view<std::ranges::owning_view<BaseRange>> >);
+ // Verify begin() produces the first element.
assert(*copied_stride_base_range.begin() == 1);
assert(*moved_stride_base_range.begin() == 1);
-
assert(*copied_stride_base_view.begin() == 1);
assert(*moved_stride_base_view.begin() == 1);
+ // Verify iteration with stride 2 over a range.
auto copied_stride_range_it = copied_stride_base_range.begin();
copied_stride_range_it++;
assert(*copied_stride_range_it == 3);
@@ -73,6 +78,7 @@ constexpr bool test() {
moved_stride_range_it++;
assert(moved_stride_range_it == moved_stride_base_range.end());
+ // Verify iteration with stride 2 over a view.
auto copied_stride_view_it = copied_stride_base_view.begin();
copied_stride_view_it++;
assert(*copied_stride_view_it == 3);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
index b41e5e809d232..8ff456efe306d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
@@ -18,44 +18,47 @@
#include "test_iterators.h"
#include "types.h"
-// There is no default ctor for stride_view.
-using View = BasicTestView<cpp17_input_iterator<int*>>;
-static_assert(!std::is_default_constructible_v<std::ranges::stride_view<View>>);
+constexpr bool test() {
+ {
+ // There is no default ctor for stride_view.
+ using View = BasicTestView<cpp17_input_iterator<int*>>;
+ static_assert(!std::is_default_constructible_v<std::ranges::stride_view<View>>);
-// Test that the stride_view can only be explicitly constructed.
-static_assert(!test_convertible<std::ranges::stride_view<View>, View, int>());
+ // Test that the stride_view can only be explicitly constructed.
+ static_assert(!test_convertible<std::ranges::stride_view<View>, View, int>());
+ }
-constexpr bool test() {
{
int arr[] = {1, 2, 3};
// Test that what we will stride over is move only.
- static_assert(!std::is_copy_constructible_v<MoveOnlyView<cpp17_input_iterator<int*>>>);
- static_assert(std::is_move_constructible_v<MoveOnlyView<cpp17_input_iterator<int*>>>);
+ using View = MoveOnlyView<cpp17_input_iterator<int*>>;
+ static_assert(!std::is_copy_constructible_v<View>);
+ static_assert(std::is_move_constructible_v<View>);
- MoveOnlyView<cpp17_input_iterator<int*>> mov(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3));
+ View mov(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3));
// Because MoveOnlyView is, well, move only, we can test that it is moved
// from when the stride view is constructed.
- std::ranges::stride_view<MoveOnlyView<cpp17_input_iterator<int*>>> mosv(std::move(mov), 1);
+ std::ranges::stride_view<View> strided(std::move(mov), 1);
// While we are here, make sure that the ctor captured the stride.
- assert(mosv.stride() == 1);
+ assert(strided.stride() == 1);
}
{
// Verify salient properties after construction.
- int arr[] = {10, 20, 30, 40, 50};
- using Base = BasicTestView<int*, int*>;
- auto sv = std::ranges::stride_view(Base(arr, arr + 5), 2);
+ int arr[] = {10, 20, 30, 40, 50};
+ using Base = BasicTestView<int*, int*>;
+ auto strided = std::ranges::stride_view(Base(arr, arr + 5), 2);
- assert(sv.stride() == 2);
- assert(*sv.begin() == 10);
+ assert(strided.stride() == 2);
+ assert(*strided.begin() == 10);
- auto it = sv.begin();
+ auto it = strided.begin();
++it;
assert(*it == 30);
++it;
assert(*it == 50);
++it;
- assert(it == sv.end());
+ assert(it == strided.end());
}
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
index 5bfd19eebdd7c..138552d3efcbc 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
@@ -140,7 +140,8 @@ struct NotSimpleViewDifferentEnd : std::ranges::view_base {
constexpr bool test() {
{
- using NotSimpleStrideView = std::ranges::stride_view<NotSimpleViewDifferentBegin<false>>;
+ // Non-simple view produces distinct const vs non-const iterator types.
+ using NotSimpleStrideView = std::ranges::stride_view<NotSimpleViewDifferentBegin</*CopyConvertible=*/false>>;
using NotSimpleStrideViewIter = std::ranges::iterator_t<NotSimpleStrideView>;
using NotSimpleStrideViewIterConst = std::ranges::iterator_t<const NotSimpleStrideView>;
static_assert(!std::is_same_v<NotSimpleStrideViewIterConst, NotSimpleStrideViewIter>);
@@ -180,10 +181,8 @@ constexpr bool test() {
// ---------------------------------
{
- // Stride over non-simple view over whose iterators are copy convertible -- should look (statically)
- // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
- // a move of the current between the copied-from iterator to the copied-to iterator).
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<true, false>;
+ // Copy-convertible iterators, different end: conversion is possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd</*CopyConvertible=*/true, /*MoveConvertible=*/false>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -200,10 +199,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are move convertible -- should look (statically)
- // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
- // a move of the current between the copied-from iterator to the copied-to iterator).
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, true>;
+ // Move-convertible iterators, different end: conversion is possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd</*CopyConvertible=*/false, /*MoveConvertible=*/true>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -220,9 +217,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are not convertible -- should not be able
- // to copy construct the stride view's iterator.
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, false>;
+ // Non-convertible iterators, different end: conversion is NOT possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd</*CopyConvertible=*/false, /*MoveConvertible=*/false>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -239,9 +235,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are not convertible -- should not be able
- // to copy construct the stride view's iterator.
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd<false, true>;
+ // Move-convertible iterators, different end: runtime construction test.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd</*CopyConvertible=*/false, /*MoveConvertible=*/true>;
using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -269,10 +264,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are copy convertible -- should look (statically)
- // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
- // a move of the current between the copied-from iterator to the copied-to iterator).
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<true, false>;
+ // Copy-convertible iterators, different begin: conversion is possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin</*CopyConvertible=*/true, /*MoveConvertible=*/false>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -289,10 +282,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are move convertible -- should look (statically)
- // like it is possible copy construct the stride view's iterator (the move-only requirement comes from
- // a move of the current between the copied-from iterator to the copied-to iterator).
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, true>;
+ // Move-convertible iterators, different begin: conversion is possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin</*CopyConvertible=*/false, /*MoveConvertible=*/true>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -309,9 +300,8 @@ constexpr bool test() {
}
{
- // Stride over non-simple view over whose iterators are not convertible -- should not be able
- // to copy construct the stride view's iterator.
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, false>;
+ // Non-convertible iterators, different begin: conversion is NOT possible.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin</*CopyConvertible=*/false, /*MoveConvertible=*/false>;
// using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
// using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
@@ -328,9 +318,8 @@ constexpr bool test() {
}
{
- // The NotSimpleViewBeingStrided template parameters mean that NotSimpleViewBeingStridedIterator
- // can be move-converted to NotSimpleViewBeingStridedConstIterator but not copy-converted.
- using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin<false, true>;
+ // Move-convertible iterators, different begin: runtime construction test with full verification.
+ using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin</*CopyConvertible=*/false, /*MoveConvertible=*/true>;
using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t<NotSimpleViewBeingStrided>;
using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t<const NotSimpleViewBeingStrided>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
index c410644fe8e7a..d93369345a89e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -26,13 +26,9 @@ struct ViewWithNonDefaultConstructibleIterator : std::ranges::view_base {
template <>
inline constexpr bool std::ranges::enable_borrowed_range<ViewWithNonDefaultConstructibleIterator> = true;
-// If the type of the iterator of the range being strided is non-default
-// constructible, then the stride view's iterator should not be default
-// constructible, either!
+// The stride_view iterator is default-constructible iff the iterator type of the range being
+// strided is default-constructible.
static_assert(!std::is_default_constructible< std::ranges::iterator_t<ViewWithNonDefaultConstructibleIterator>>());
-// If the type of the iterator of the range being strided is default
-// constructible, then the stride view's iterator should be default
-// constructible, too!
static_assert(std::is_default_constructible<
std::ranges::iterator_t< std::ranges::stride_view<std::ranges::ref_view<const int[3]>>>>());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
index 131743d582055..e0df34978dcb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
@@ -86,7 +86,8 @@ constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) {
constexpr bool test() {
{
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ // Pre-decrement and post-decrement from end with stride 3.
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
test_operator_decrement(arr, arr + 11, 3);
}
return true;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
index 4bf2f04e64f8c..cd1dd5176fb26 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -37,7 +37,7 @@ constexpr void testOne() {
using StrideView = std::ranges::stride_view<Range>;
{
- // simple test
+ // Equality and inequality between iterators, and comparison with end.
{
int buffer[] = {0, 1, 2, -1, 4, 5, 6, 7};
const Range input(Iter{buffer}, Iter{buffer + 8});
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
index 2c3fbcc48554c..f259839020e34 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
@@ -148,8 +148,8 @@ constexpr bool test_forward_operator_increment(Iter begin, Iter end) {
constexpr bool test_properly_handling_missing() {
// Check whether the "missing" distance to the end gets handled properly.
- using Base = BasicTestView<int*, int*>;
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ using Base = BasicTestView<int*, int*>;
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto base = Base(arr, arr + 10);
auto strider = std::ranges::stride_view<Base>(base, 7);
@@ -189,7 +189,7 @@ constexpr bool test_properly_handling_missing() {
constexpr bool test() {
{
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
test_forward_operator_increment(arr, arr + 11);
}
test_properly_handling_missing();
@@ -202,7 +202,7 @@ int main(int, char**) {
// Non-forward iterators can't be tested in a constexpr context.
{
- int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
test_non_forward_operator_increment(SizedInputIter(arr), SizedInputIter(arr + 3), SizedInputIter(arr + 10));
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
index adacd6f7eb0d1..aebae80005eca 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
@@ -23,10 +23,11 @@ concept iter_moveable = requires(T&& t) { std::ranges::iter_move(t); };
constexpr bool test() {
{
+ // iter_move with a noexcept iter_move on the base iterator.
int a[] = {4, 3, 2, 1};
int iter_move_counter(0);
- using View = IterMoveIterSwapTestRange<int*, true, true>;
+ using View = IterMoveIterSwapTestRange<int*, /*IsSwappable=*/true, /*IsNoExcept=*/true>;
using StrideView = std::ranges::stride_view<View>;
auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
@@ -45,10 +46,11 @@ constexpr bool test() {
}
{
+ // iter_move with a potentially-throwing iter_move on the base iterator.
int a[] = {1, 2, 3, 4};
int iter_move_counter(0);
- using View = IterMoveIterSwapTestRange<int*, true, false>;
+ using View = IterMoveIterSwapTestRange<int*, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin();
@@ -67,10 +69,11 @@ constexpr bool test() {
}
{
+ // iter_move with a non-pointer base iterator (vector::iterator).
std::vector<int> a = {4, 5, 6, 7, 8};
int iter_move_counter(0);
- using View = IterMoveIterSwapTestRange<std::vector<int>::iterator, true, false>;
+ using View = IterMoveIterSwapTestRange<std::vector<int>::iterator, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
auto svb = StrideView(View(a.begin(), a.end(), &iter_move_counter), 1).begin();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
index 493bff0671c0c..23846a1c2f9d3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
@@ -18,21 +18,22 @@
#include "../types.h"
template <typename T>
-concept swappable = requires(T&& t, T&& u) { std::ranges::iter_swap(t, u); };
+concept Swappable = requires(T&& t, T&& u) { std::ranges::iter_swap(t, u); };
constexpr bool test() {
{
+ // iter_swap with a noexcept iter_swap on the base iterator.
int a[] = {1, 2, 3, 4};
int b[] = {5, 6, 7, 8};
int iter_move_counter_one(0);
int iter_move_counter_two(0);
- using View = IterMoveIterSwapTestRange<int*, true, true>;
+ using View = IterMoveIterSwapTestRange<int*, /*IsSwappable=*/true, /*IsNoExcept=*/true>;
using StrideView = std::ranges::stride_view<View>;
auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin();
auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin();
- static_assert(swappable<std::ranges::iterator_t<StrideView>>);
+ static_assert(Swappable<std::ranges::iterator_t<StrideView>>);
static_assert(noexcept(std::ranges::iter_swap(svba, svbb)));
assert(a[0] == 1);
@@ -47,17 +48,18 @@ constexpr bool test() {
}
{
+ // iter_swap with a potentially-throwing iter_swap on the base iterator.
int a[] = {1, 2, 3, 4};
int b[] = {5, 6, 7, 8};
int iter_move_counter_one(0);
int iter_move_counter_two(0);
- using View = IterMoveIterSwapTestRange<int*, true, false>;
+ using View = IterMoveIterSwapTestRange<int*, /*IsSwappable=*/true, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin();
auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin();
- static_assert(swappable<std::ranges::iterator_t<StrideView>>);
+ static_assert(Swappable<std::ranges::iterator_t<StrideView>>);
static_assert(!noexcept(std::ranges::iter_swap(svba, svbb)));
assert(a[0] == 1);
@@ -72,10 +74,11 @@ constexpr bool test() {
}
{
- using View = IterMoveIterSwapTestRange<int*, false, false>;
+ // iter_swap is not available when the base iterator is not indirectly_swappable.
+ using View = IterMoveIterSwapTestRange<int*, /*IsSwappable=*/false, /*IsNoExcept=*/false>;
using StrideView = std::ranges::stride_view<View>;
- static_assert(!swappable<std::ranges::iterator_t<StrideView>>);
+ static_assert(!Swappable<std::ranges::iterator_t<StrideView>>);
}
return true;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
index 5018bd0ebb8a0..ee983f531af9f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
@@ -60,8 +60,6 @@ static_assert(CanPlus<StrideViewOverRandomAccessViewIterator>);
constexpr bool test() {
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
- // Test the operator+ between an iterator and its difference type. Pay attention solely to
- // stride views over random-access ranges because operator+ is not applicable to others.
auto begin = vec.begin();
auto end = vec.end();
@@ -70,20 +68,28 @@ constexpr bool test() {
using Base = RandomAccessView<std::vector<int>::iterator>;
static_assert(std::ranges::random_access_range<Base>);
- auto base_view = Base(begin, end);
- auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
-
- auto base_view_offset = Base(begin + distance, end);
- auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1);
-
- assert(*(stride_view_over_base_view.begin() + distance) == *(stride_view_over_base_view_offset.begin()));
-
- auto distance_to_last = (end - 1) - begin;
- auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, distance_to_last);
-
- assert(*(stride_view_over_base_view_big_step.begin() + 1) ==
- *(stride_view_over_base_view.begin() + distance_to_last));
- assert((stride_view_over_base_view_big_step.begin() + 2) == (stride_view_over_base_view.end()));
+ {
+ // iterator + distance produces the same element as starting at begin + distance.
+ auto base_view = Base(begin, end);
+ auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
+
+ auto base_view_offset = Base(begin + distance, end);
+ auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1);
+
+ assert(*(stride_view_over_base_view.begin() + distance) == *(stride_view_over_base_view_offset.begin()));
+ }
+ {
+ // iterator + 1 with a large stride reaches the same element as iterator + stride with stride 1.
+ auto base_view = Base(begin, end);
+ auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
+ auto distance_to_last = (end - 1) - begin;
+ auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, distance_to_last);
+
+ assert(*(stride_view_over_base_view_big_step.begin() + 1) ==
+ *(stride_view_over_base_view.begin() + distance_to_last));
+ // iterator + 2 past the end of a large-stride view equals the end of the stride-1 view.
+ assert((stride_view_over_base_view_big_step.begin() + 2) == (stride_view_over_base_view.end()));
+ }
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
index fe302fee565d7..6ce4788e0f863 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
@@ -68,6 +68,7 @@ constexpr bool test() {
using Base = RandomAccessView<Iter>;
static_assert(std::ranges::random_access_range<Base>);
+ // += with stride 1: advancing by distance matches starting at begin + distance.
auto base_view = Base(begin, end);
auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1);
@@ -81,6 +82,7 @@ constexpr bool test() {
assert(*sv_bv_begin == *sv_bv_offset_begin);
assert(*sv_bv_begin_after_distance == *sv_bv_offset_begin);
+ // += past the end, then -= back: the remainder is handled correctly.
auto big_step = (end - 1) - begin;
auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, big_step);
sv_bv_begin = stride_view_over_base_view_big_step.begin();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
index 28159cc153c1b..4c0e7c1728355 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -23,26 +23,27 @@ static_assert(!std::ranges::sized_range<std::ranges::stride_view<BasicTestView<c
// There is a size member function on a stride view over a view that
// *is* a sized range
-static_assert(std::ranges::sized_range<BasicTestView<int*, sentinel_wrapper<int*>, true>>);
-static_assert(std::ranges::sized_range<std::ranges::stride_view<BasicTestView<int*, sentinel_wrapper<int*>, true>>>);
+static_assert(std::ranges::sized_range<BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/true>>);
+static_assert(
+ std::ranges::sized_range<std::ranges::stride_view<BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/true>>>);
constexpr bool test() {
{
// Test with stride as exact multiple of number of elements in view strided over.
- constexpr auto iota_twelve = std::views::iota(0, 12);
- static_assert(std::ranges::sized_range<decltype(iota_twelve)>);
- constexpr auto stride_iota_twelve = std::views::stride(iota_twelve, 3);
- static_assert(std::ranges::sized_range<decltype(stride_iota_twelve)>);
- static_assert(4 == stride_iota_twelve.size(), "Striding by 3 through a 12 member list has size 4.");
+ auto iota = std::views::iota(0, 12);
+ static_assert(std::ranges::sized_range<decltype(iota)>);
+ auto strided = std::views::stride(iota, 3);
+ static_assert(std::ranges::sized_range<decltype(strided)>);
+ assert(strided.size() == 4);
}
{
// Test with stride as inexact multiple of number of elements in view strided over.
- constexpr auto iota_twenty_two = std::views::iota(0, 22);
- static_assert(std::ranges::sized_range<decltype(iota_twenty_two)>);
- constexpr auto stride_iota_twenty_two = std::views::stride(iota_twenty_two, 3);
- static_assert(std::ranges::sized_range<decltype(stride_iota_twenty_two)>);
- static_assert(8 == stride_iota_twenty_two.size(), "Striding by 3 through a 22 member list has size 8.");
+ constexpr auto iota = std::views::iota(0, 22);
+ static_assert(std::ranges::sized_range<decltype(iota)>);
+ constexpr auto strided = std::views::stride(iota, 3);
+ static_assert(std::ranges::sized_range<decltype(strided)>);
+ assert(strided.size() == 8);
}
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
index 9266e6840fccf..6ec8b834eb9b7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
@@ -18,17 +18,13 @@
constexpr bool test() {
using View = BasicTestView<cpp17_input_iterator<int*>>;
- int arr[]{1, 2, 3};
- auto arrv(View(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3)));
- // Mark str const so that we confirm that stride is a const member function.
- const std::ranges::stride_view<View> str(arrv, 1);
-
- // Make sure that stride member function is noexcept.
- static_assert(noexcept(str.stride()));
- // Make sure that the type returned by stride matches what is expected.
- ASSERT_SAME_TYPE(std::ranges::range_difference_t<View>, decltype(str.stride()));
- // Make sure that we get back a stride equal to the one that we gave in the ctor.
- assert(str.stride() == 1);
+ int arr[] = {1, 2, 3};
+ View view(cpp17_input_iterator<int*>(arr), cpp17_input_iterator<int*>(arr + 3));
+
+ const std::ranges::stride_view<View> strided(view, 2);
+ static_assert(noexcept(strided.stride()));
+ ASSERT_SAME_TYPE(std::ranges::range_difference_t<View>, decltype(strided.stride()));
+ assert(strided.stride() == 2);
return true;
}
More information about the libcxx-commits
mailing list