[libcxx-commits] [libcxx] 2f28e1d - [libc++] Implement P1899 `ranges::stride_view` (#65200)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 25 03:51:43 PDT 2026


Author: Will Hawkins
Date: 2026-04-25T06:51:37-04:00
New Revision: 2f28e1db535bddca42d0954b365a4e5f17bdf534

URL: https://github.com/llvm/llvm-project/commit/2f28e1db535bddca42d0954b365a4e5f17bdf534
DIFF: https://github.com/llvm/llvm-project/commit/2f28e1db535bddca42d0954b365a4e5f17bdf534.diff

LOG: [libc++] Implement P1899 `ranges::stride_view` (#65200)

Implement `ranges::stride_view` in libc++. This PR was migrated from
Phabricator (https://reviews.llvm.org/D156924).

Closes #105198

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
Co-authored-by: A. Jiang <de34 at live.cn>

Added: 
    libcxx/include/__ranges/stride_view.h
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/23.rst
    libcxx/docs/Status/Cxx23Papers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/module.modulemap.in
    libcxx/include/ranges
    libcxx/include/version
    libcxx/modules/std/ranges.inc
    libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 5f7d90e8eca5e..1ee5ec7f72ee9 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -390,6 +390,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_starts_ends_with``                      ``202106L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_stride``                                ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_to_container``                          ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_zip``                                   ``202110L``

diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 0160ea2179ccc..8e9b1e749d619 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)
 - P4052R0: Renaming saturation arithmetic functions (`Github <https://llvm.org/PR189589>`__)

diff  --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 80ed7dba3ead7..41b1b8f884eff 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/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 95d822ee8c0b3..69a6590d18f85 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -751,6 +751,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..780bb25743c15
--- /dev/null
+++ b/libcxx/include/__ranges/stride_view.h
@@ -0,0 +1,410 @@
+// -*- 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 <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.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_
diff erence_t<_View> __stride_     = 0;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_
diff erence_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_
diff erence_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<const _View> && !bidirectional_range<const _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_
diff erence_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<__maybe_const<_Const, _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_
diff erence_t<_Base> __stride_                        = 0;
+  range_
diff erence_t<_Base> __missing_                       = 0;
+
+  friend stride_view;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, ranges::iterator_t<_Base> __current, range_
diff erence_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 
diff erence_type  = range_
diff erence_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+=(
diff erence_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-=(
diff erence_type __n)
+    requires random_access_range<_Base>
+  {
+    return *this += -__n;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](
diff erence_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, 
diff erence_type __s)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __s;
+    return __r;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(
diff erence_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, 
diff erence_type __s)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __s;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_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 
diff erence_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 
diff erence_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_
diff erence_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 234c230ee38d0..b7c2ef91be551 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,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

diff  --git a/libcxx/include/ranges b/libcxx/include/ranges
index 782b4a77c0367..308224427db60 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -407,6 +407,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 {
@@ -497,6 +508,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 b462fba39cd19..7278be80b7f64 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -218,6 +218,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>
@@ -534,6 +535,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 97513bc686394..576f0650438e3 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -341,7 +341,6 @@ export namespace std {
       using std::ranges::views::chunk_by;
     }
 
-#  if 0
     // [range.stride], stride view
     using std::ranges::stride_view;
 
@@ -349,6 +348,7 @@ export namespace std {
       using std::ranges::views::stride;
     }
 
+#  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..e564891b15a15
--- /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
+//
+//===----------------------------------------------------------------------===//
+
+// 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
+
+// constexpr explicit stride_view(_View, range_
diff erence_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.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.pass.cpp
new file mode 100644
index 0000000000000..6884c27f5197c
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.assert.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
+//
+//===----------------------------------------------------------------------===//
+
+// 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
+
+// constexpr decltype(auto) operator*() const
+
+#include <ranges>
+
+#include "check_assertion.h"
+
+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.");
+  }
+  {
+    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.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.pass.cpp
new file mode 100644
index 0000000000000..1c580fea4a310
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.assert.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
+//
+//===----------------------------------------------------------------------===//
+
+// 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
+
+// constexpr __iterator& operator++()
+// constexpr void operator++(int)
+// constexpr __iterator operator++(int)
+
+#include <ranges>
+
+#include "check_assertion.h"
+#include "test_iterators.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 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.");
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
+  }
+  {
+    int range[] = {1, 2, 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.");
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end.");
+  }
+  {
+    int range[] = {1, 2, 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.");
+  }
+  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..152779ec3da85
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iterator.nodiscard.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// 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>
+
+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}}
+  it.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(it).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(it);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  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
new file mode 100644
index 0000000000000..0a84436444cd5
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.assert.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
+//
+//===----------------------------------------------------------------------===//
+
+// 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
+
+// constexpr __iterator& operator+=(
diff erence_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..9c48ecb68e5e3
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Test that stride_view's member functions are properly marked nodiscard.
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  int range[] = {1, 2, 3};
+  auto sv     = std::ranges::stride_view(range, 2);
+
+  // 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();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  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}}
+  sv.stride();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::stride(range, 2);
+}

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 7d763f877b321..6825f9675d459 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
@@ -68,6 +68,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
@@ -126,6 +130,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
@@ -184,6 +192,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
@@ -245,6 +257,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
@@ -348,6 +364,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
@@ -478,6 +501,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 93a156bd9a6be..a403c0c708a70 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
@@ -704,6 +704,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
@@ -1668,6 +1672,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
@@ -2797,6 +2805,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
@@ -4193,6 +4205,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
@@ -5808,6 +5824,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
@@ -7747,6 +7770,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..02b564bda7e0f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -0,0 +1,118 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::views::stride
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+
+#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
+
+  // Test `view | views::stride`
+  {
+    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]);
+  }
+
+  // Test `adaptor | views::stride`
+  auto twice = [](int i) { return i * 2; };
+  {
+    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);
+
+    std::same_as<View> decltype(auto) result = partial(view);
+    auto it                                  = result.begin();
+
+    assert(*it == twice(arr[0]));
+    std::ranges::advance(it, 1);
+    assert(*it == twice(arr[2]));
+  }
+
+  // Test `views::stride | adaptor`
+  {
+    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 it = result.begin();
+
+    assert(*it == twice(arr[0]));
+    std::ranges::advance(it, 1);
+    assert(*it == twice(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..bb1cc617e68dc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 V base() const& requires copy_constructible<V>;
+// constexpr V base() &&;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <typename 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
+  // (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 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(view));
+
+    std::same_as<CopyableInputView> decltype(auto) s = view.base();
+    assert(*s.begin() == *buff);
+  }
+
+  // &
+  {
+    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 = view.base();
+    assert(*s.begin() == *buff);
+
+    static_assert(has_lvalue_qualified_base(view));
+  }
+
+  // r-value ref qualified
+  // &&
+  {
+    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(view));
+
+    std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
+    assert(*s.begin() == *buff);
+  }
+
+  // const &&
+  {
+    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(view));
+
+    std::same_as<CopyableInputView> decltype(auto) s = std::move(view).base();
+    assert(*s.begin() == *buff);
+  }
+
+  // &&
+  {
+    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(view));
+
+    std::same_as<MoveOnlyInputView> decltype(auto) s = std::move(view).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..ca45fd81829c8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 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>
+
+#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 
diff erent 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() {
+  {
+    // 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);
+  }
+  {
+    // Empty range: begin() == end().
+    int data[] = {1};
+    auto v     = BasicTestView<int*, int*>{data, data};
+    auto sv    = std::ranges::stride_view(v, 3);
+    assert(sv.begin() == sv.end());
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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..cfadffbda28de
--- /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
+//
+//===----------------------------------------------------------------------===//
+
+// 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>;
+
+#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..afc1d16825c07
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <input_range V> requires view<V>
+
+#include <ranges>
+#include <type_traits>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <typename I, std::ranges::range_
diff erence_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 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_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>);
+
+// 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>);
+
+// stride_view itself models view.
+static_assert(std::ranges::view<std::ranges::stride_view<test_view<cpp17_input_iterator>>>);
+static_assert(std::ranges::view<std::ranges::stride_view<test_view<forward_iterator>>>);

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..12653c4c5dd07
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template <class R>
+// stride_view(R&&, range_
diff erence_t<R>) -> stride_view<views::all_t<R>>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "types.h"
+
+struct View : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct Range {
+  int* begin() const;
+  int* end() const;
+};
+
+constexpr bool test() {
+  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_move = BaseView(a, a + 5);
+
+  auto base_range      = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
+  auto base_range_move = BaseRange(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 5));
+
+  // Deduction from lvalue view and rvalue view.
+  auto sv_view      = std::ranges::stride_view(base_view, 2);
+  auto sv_view_move = std::ranges::stride_view(std::move(base_view_move), 2);
+
+  // Deduction from lvalue range (-> ref_view) and rvalue range (-> owning_view).
+  auto sv_range      = std::ranges::stride_view(base_range, 2);
+  auto sv_range_move = std::ranges::stride_view(std::move(base_range_move), 2);
+
+  // Verify deduced types for views.
+  static_assert(std::same_as<decltype(sv_view), std::ranges::stride_view<BaseView>>);
+  static_assert(std::same_as<decltype(sv_view_move), std::ranges::stride_view<BaseView>>);
+
+  // Verify deduced types for ranges: lvalue -> ref_view, rvalue -> owning_view.
+  static_assert(std::same_as<decltype(sv_range), std::ranges::stride_view<std::ranges::ref_view<BaseRange>> >);
+  static_assert(std::same_as<decltype(sv_range_move), std::ranges::stride_view<std::ranges::owning_view<BaseRange>> >);
+
+  // Verify begin() produces the first element.
+  assert(*sv_range.begin() == 1);
+  assert(*sv_range_move.begin() == 1);
+  assert(*sv_view.begin() == 1);
+  assert(*sv_view_move.begin() == 1);
+
+  // Verify iteration with stride 2 over a range.
+  auto it = sv_range.begin();
+  it++;
+  assert(*it == 3);
+  it++;
+  it++;
+  assert(it == sv_range.end());
+
+  auto it2 = sv_range_move.begin();
+  it2++;
+  it2++;
+  assert(*it2 == 5);
+  it2++;
+  assert(it2 == sv_range_move.end());
+
+  // Verify iteration with stride 2 over a view.
+  auto it3 = sv_view.begin();
+  it3++;
+  assert(*it3 == 3);
+  it3++;
+  it3++;
+  assert(it3 == sv_view.end());
+
+  auto it4 = sv_view.begin();
+  it4++;
+  it4++;
+  assert(*it4 == 5);
+  it4++;
+  assert(it4 == sv_view_move.end());
+
+  return true;
+}
+
+int main(int, char**) {
+  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
new file mode 100644
index 0000000000000..6e7f6c92c46f7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 explicit stride_view(V base, range_
diff erence_t<V> stride)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_convertible.h"
+#include "test_iterators.h"
+#include "types.h"
+
+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>());
+  }
+
+  {
+    int arr[] = {1, 2, 3};
+    // Test that what we will stride over is move only.
+    using View = MoveOnlyView<cpp17_input_iterator<int*>>;
+    static_assert(!std::is_copy_constructible_v<View>);
+    static_assert(std::is_move_constructible_v<View>);
+
+    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<View> strided(std::move(mov), 1);
+
+    // While we are here, make sure that the ctor captured the stride.
+    assert(strided.stride() == 1);
+  }
+  {
+    // Verify salient properties after construction.
+    int arr[]    = {10, 20, 30, 40, 50};
+    using Base   = BasicTestView<int*, int*>;
+    auto strided = std::ranges::stride_view(Base(arr, arr + 5), 2);
+
+    assert(strided.stride() == 2);
+    assert(*strided.begin() == 10);
+
+    auto it = strided.begin();
+    ++it;
+    assert(*it == 30);
+    ++it;
+    assert(*it == 50);
+    ++it;
+    assert(it == strided.end());
+  }
+  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..2f4cc5f87546f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
@@ -0,0 +1,222 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 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 
diff erent 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(); };
+
+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() {
+  {
+    // A const, simple, common-, sized- and forward-range.
+    // Note: sized because it is possible to get a 
diff erence 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(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());
+  }
+  {
+    // 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 
diff erence 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(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());
+  }
+  {
+    // 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>);
+
+    auto sv = std::ranges::stride_view<UnSimpleNoConstCommonView>(UnSimpleNoConstCommonView{}, 1);
+    static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
+  }
+  {
+    // Uncommon range -> returns default_sentinel.
+    static_assert(!simple_view<UnsimpleUnCommonConstView>);
+    static_assert(!std::ranges::common_range<UnsimpleUnCommonConstView>);
+
+    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 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());
+  }
+  {
+    // 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.
+    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());
+  }
+  {
+    // Test the `common_range && forward_range && !sized_range && !bidirectional_range` branch.
+    // forward_iterator<int*> does not support operator-, so the view is not sized.
+    // end() should return an iterator (not default_sentinel).
+    int data[]    = {1, 2, 3, 4, 5};
+    using FwdView = BasicTestView<forward_iterator<int*>, forward_iterator<int*>>;
+    static_assert(std::ranges::common_range<FwdView>);
+    static_assert(std::ranges::forward_range<FwdView>);
+    static_assert(!std::ranges::bidirectional_range<FwdView>);
+    static_assert(!std::ranges::sized_range<FwdView>);
+
+    auto v  = FwdView{forward_iterator(data), forward_iterator(data + 5)};
+    auto sv = std::ranges::stride_view(v, 2);
+    static_assert(!std::is_same_v<std::default_sentinel_t, decltype(sv.end())>);
+
+    auto it = sv.begin();
+    assert(*it == 1);
+    ++it;
+    assert(*it == 3);
+    ++it;
+    assert(*it == 5);
+    ++it;
+    assert(it == sv.end());
+  }
+  {
+    // Empty range: begin() == end().
+    int data[] = {1};
+    using Base = BasicTestView<int*, int*>;
+    auto sv    = std::ranges::stride_view(Base(data, data), 3);
+    assert(sv.begin() == sv.end());
+  }
+  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/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..82acab6d6aeed
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.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 const iterator_t<Base>& base() const& noexcept
+// constexpr iterator_t<Base> base() &&
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+
+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();
+
+    static_assert(noexcept(stride_iter.base()));
+    static_assert(!noexcept((std::move(stride_iter).base())));
+  }
+  {
+    // 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();
+
+    static_assert(std::is_const_v<std::remove_reference_t<decltype(stride_iter.base())>>);
+    static_assert(!std::is_const_v<decltype(std::move(stride_iter).base())>);
+  }
+  {
+    // base() && moves the underlying iterator.
+    int move_counter = 0;
+    int copy_counter = 0;
+
+    auto start         = SizedInputIter();
+    start.move_counter = &move_counter;
+    start.copy_counter = &copy_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 = &copy_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**) {
+  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..6f4e098e664f8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/compare.pass.cpp
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <compare>
+#include <functional>
+#include <ranges>
+#include <type_traits>
+
+#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
new file mode 100644
index 0000000000000..298e7d6e36dd5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+//
+// 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(iterator<!Const> i)
+//    requires Const && convertible_to<iterator_t<V>, iterator_t<Base>> &&
+//                 convertible_to<sentinel_t<V>, sentinel_t<Base>>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "../types.h"
+
+// A non-simple view over actual data. begin()/end() return int* when non-const
+// and const int* when const, so iterator<false> and iterator<true> are distinct
+// and the converting constructor is available (int* converts to const int*).
+struct NonSimpleDataView : std::ranges::view_base {
+  int* data_;
+  int size_;
+
+  constexpr NonSimpleDataView(int* d, int s) : data_(d), size_(s) {}
+  NonSimpleDataView(NonSimpleDataView&&)            = default;
+  NonSimpleDataView& operator=(NonSimpleDataView&&) = default;
+
+  constexpr int* begin() { return data_; }
+  constexpr const int* begin() const { return data_; }
+  constexpr int* end() { return data_ + size_; }
+  constexpr const int* end() const { return data_ + size_; }
+};
+
+static_assert(!simple_view<NonSimpleDataView>);
+
+// Two unrelated input iterator types (no conversion between them).
+struct IterA : InputIter<IterA> {};
+struct IterB : InputIter<IterB> {
+  friend constexpr bool operator==(const IterB&, const IterA&) { return true; }
+};
+
+// Two unrelated sentinel types (no conversion between them).
+struct SentA {
+  friend constexpr bool operator==(const SentA&, const IterA&) { return true; }
+  friend constexpr bool operator==(const SentA&, const IterB&) { return true; }
+};
+struct SentB {
+  friend constexpr bool operator==(const SentB&, const IterA&) { return true; }
+  friend constexpr bool operator==(const SentB&, const IterB&) { return true; }
+};
+
+// Non-simple view where iterator conversion fails (IterA does not convert to IterB).
+struct IterNonConvertibleView : std::ranges::view_base {
+  constexpr IterA begin() { return {}; }
+  constexpr IterB begin() const { return {}; }
+  constexpr SentA end() const { return {}; }
+};
+
+// Non-simple view where sentinel conversion fails (SentA does not convert to SentB).
+struct SentNonConvertibleView : std::ranges::view_base {
+  constexpr IterA begin() const { return {}; }
+  constexpr SentA end() { return {}; }
+  constexpr SentB end() const { return {}; }
+};
+
+// Conversion succeeds: both iterator and sentinel types are convertible (int* -> const int*).
+using ConvertibleSV    = std::ranges::stride_view<NonSimpleDataView>;
+using ConvertibleIter  = std::ranges::iterator_t<ConvertibleSV>;
+using ConvertibleCIter = std::ranges::iterator_t<const ConvertibleSV>;
+static_assert(!std::same_as<ConvertibleIter, ConvertibleCIter>);
+static_assert(std::convertible_to<ConvertibleIter, ConvertibleCIter>);
+static_assert(std::constructible_from<ConvertibleCIter, ConvertibleIter>);
+
+// Conversion fails: underlying iterator types are not convertible (IterA -> IterB).
+using IterNCSV    = std::ranges::stride_view<IterNonConvertibleView>;
+using IterNCIter  = std::ranges::iterator_t<IterNCSV>;
+using IterNCCIter = std::ranges::iterator_t<const IterNCSV>;
+static_assert(!std::same_as<IterNCIter, IterNCCIter>);
+static_assert(!std::convertible_to<IterNCIter, IterNCCIter>);
+static_assert(!std::constructible_from<IterNCCIter, IterNCIter>);
+
+// Conversion fails: underlying sentinel types are not convertible (SentA -> SentB).
+using SentNCSV    = std::ranges::stride_view<SentNonConvertibleView>;
+using SentNCIter  = std::ranges::iterator_t<SentNCSV>;
+using SentNCCIter = std::ranges::iterator_t<const SentNCSV>;
+static_assert(!std::same_as<SentNCIter, SentNCCIter>);
+static_assert(!std::convertible_to<SentNCIter, SentNCCIter>);
+static_assert(!std::constructible_from<SentNCCIter, SentNCIter>);
+
+constexpr bool test() {
+  {
+    // Convert non-const iterator to const iterator with stride 1.
+    int arr[] = {10, 20, 30, 40, 50};
+    auto sv   = ConvertibleSV(NonSimpleDataView(arr, 5), 1);
+    auto it   = sv.begin();
+    assert(*it == 10);
+
+    ConvertibleCIter cit{it};
+    assert(*cit == 10);
+
+    ++cit;
+    assert(*cit == 20);
+    ++cit;
+    assert(*cit == 30);
+    ++cit;
+    assert(*cit == 40);
+    ++cit;
+    assert(*cit == 50);
+    ++cit;
+    assert(cit == std::as_const(sv).end());
+  }
+  {
+    // Convert non-const iterator to const iterator with stride 2.
+    int arr[] = {10, 20, 30, 40, 50};
+    auto sv   = ConvertibleSV(NonSimpleDataView(arr, 5), 2);
+    auto it   = sv.begin();
+
+    ConvertibleCIter cit{it};
+    assert(*cit == 10);
+    ++cit;
+    assert(*cit == 30);
+    ++cit;
+    assert(*cit == 50);
+    ++cit;
+    assert(cit == std::as_const(sv).end());
+  }
+  {
+    // Convert after advancing the non-const iterator.
+    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    auto sv   = ConvertibleSV(NonSimpleDataView(arr, 9), 3);
+    auto it   = sv.begin();
+    ++it;
+    assert(*it == 4);
+
+    ConvertibleCIter cit{it};
+    assert(*cit == 4);
+    ++cit;
+    assert(*cit == 7);
+    ++cit;
+    assert(cit == std::as_const(sv).end());
+  }
+  {
+    // Convert with stride larger than range size.
+    int arr[] = {42, 99};
+    auto sv   = ConvertibleSV(NonSimpleDataView(arr, 2), 5);
+    auto it   = sv.begin();
+
+    ConvertibleCIter cit{it};
+    assert(*cit == 42);
+    ++cit;
+    assert(cit == std::as_const(sv).end());
+  }
+  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.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..b7fbb8a12ed3f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <ranges>
+#include <type_traits>
+
+#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;
+
+// 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>>());
+static_assert(std::is_default_constructible<
+              std::ranges::iterator_t< std::ranges::stride_view<std::ranges::ref_view<const int[3]>>>>());
+
+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
new file mode 100644
index 0000000000000..3c40e3c88f933
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp
@@ -0,0 +1,159 @@
+//===----------------------------------------------------------------------===//
+//
+// 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--()
+// constexpr iterator operator--(int)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#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;
+}
+
+constexpr bool test() {
+  {
+    // 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);
+  }
+  {
+    // Decrement from end when size % stride != 0.
+    // 10 elements, stride 3: strided elements at indices 0,3,6,9.
+    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.end();
+    --it;
+    assert(*it == 10); // index 9
+    --it;
+    assert(*it == 7); // index 6
+    --it;
+    assert(*it == 4); // index 3
+    --it;
+    assert(*it == 1); // index 0
+    assert(it == sv.begin());
+  }
+  {
+    // Decrement when stride evenly divides range size.
+    // 9 elements, stride 3: strided elements at 0,3,6.
+    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 it    = sv.end();
+    --it;
+    assert(*it == 7); // index 6
+    --it;
+    assert(*it == 4); // index 3
+    --it;
+    assert(*it == 1); // index 0
+  }
+  {
+    // Decrement from mid-range position (not end).
+    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; // index 3
+    ++it; // index 6
+    assert(*it == 7);
+    --it; // back to index 3
+    assert(*it == 4);
+    --it; // back to index 0
+    assert(*it == 1);
+  }
+  {
+    // Round-trip: decrement from end, increment, decrement again.
+    int arr[]  = {1, 2, 3, 4, 5, 6, 7};
+    using Base = BasicTestView<int*, int*>;
+    auto sv    = std::ranges::stride_view(Base(arr, arr + 7), 3);
+    auto it    = sv.end();
+    --it;
+    assert(*it == 7); // index 6
+    ++it;
+    assert(it == sv.end());
+    --it;
+    assert(*it == 7); // back to index 6 again
+  }
+  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..55b76899381ea
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 type is a reference, not a value.
+    int arr[]  = {1, 2, 3};
+    using Base = BasicTestView<int*, int*>;
+    auto sv    = std::ranges::stride_view(Base(arr, arr + 3), 1);
+    auto it    = sv.begin();
+    static_assert(std::is_same_v<decltype(*it), int&>);
+  }
+  {
+    // Dereference through a const-qualified iterator.
+    int arr[]      = {10, 20, 30};
+    using Base     = BasicTestView<int*, int*>;
+    auto sv        = std::ranges::stride_view(Base(arr, arr + 3), 2);
+    const auto cit = sv.begin();
+    assert(*cit == 10);
+  }
+  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
new file mode 100644
index 0000000000000..cd1dd5176fb26
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, 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>;
+  static_assert(std::ranges::common_range<Range>);
+  using StrideView = std::ranges::stride_view<Range>;
+
+  {
+    // 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});
+      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/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..671eca2b66a17
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp
@@ -0,0 +1,211 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++()
+// constexpr void operator++(int)
+// constexpr iterator operator++(int)
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+
+#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        = Base(zero_begin, end);
+  auto base_offset = Base(three_begin, end);
+  auto sv          = std::ranges::stride_view(base, 3);
+  auto sv_offset   = std::ranges::stride_view(base_offset, 3);
+
+  auto it        = sv.begin();
+  auto it_offset = sv_offset.begin();
+
+  auto it_after = it; // With a stride of 3, so ++ moves 3 indexes.
+  ++it_after;
+  assert(*it_offset == *it_after);
+
+  it_after = it;
+  it_after++;
+  assert(*it_offset == *it_after);
+
+  // See if both get to the end (with pre-increment).
+  auto it_to_end = it;
+  ++it_to_end; // 3
+  ++it_to_end; // 6
+  ++it_to_end; // 9
+  ++it_to_end; // End
+
+  auto it_offset_to_end = it_offset; // With a stride of 3, so ++ moves 3 indexes.
+  ++it_offset_to_end;                // 6
+  ++it_offset_to_end;                // 9
+  ++it_offset_to_end;                // End
+
+  assert(it_offset_to_end == it_to_end);
+  assert(it_offset_to_end == sv_offset.end());
+  assert(it_to_end == sv.end());
+
+  // See if both get to the end (with post-increment).
+  it_to_end = it;
+  it_to_end++; // 3
+  it_to_end++; // 6
+  it_to_end++; // 9
+  it_to_end++; // End
+
+  it_offset_to_end = it_offset; // With a stride of 3, so ++ moves 3 indexes.
+  it_offset_to_end++;           // 6
+  it_offset_to_end++;           // 9
+  it_offset_to_end++;           // End
+
+  assert(it_offset_to_end == it_to_end);
+  assert(it_offset_to_end == sv_offset.end());
+  assert(it_to_end == sv.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 = Base(begin, end);
+  auto sv   = std::ranges::stride_view(base, 3);
+  auto it   = sv.begin();
+
+  // Create a ground truth for comparison.
+  auto expected = sv.begin();
+  expected++;
+
+  auto it_after = ++it;
+  assert(*it_after == *it);
+  assert(*it_after == *expected);
+
+  it       = sv.begin();
+  it_after = it;
+  it_after++;
+  assert(*it_after == *expected);
+
+  it             = sv.begin();
+  auto it_to_end = it;
+  ++it_to_end; // 3
+  ++it_to_end; // 6
+  ++it_to_end; // 9
+  ++it_to_end; // End
+
+  auto it_to_end2 = it;           // With a stride of 3, so ++ moves 3 indexes.
+  it_to_end2      = ++it_to_end2; // 3
+  it_to_end2      = ++it_to_end2; // 6
+  it_to_end2      = ++it_to_end2; // 9
+  it_to_end2      = ++it_to_end2; // End
+
+  assert(it_to_end == it_to_end2);
+  assert(it_to_end == sv.end());
+
+  it_to_end = it;
+  it_to_end++; // 3
+  it_to_end++; // 6
+  it_to_end++; // 9
+  it_to_end++; // End
+  assert(it_to_end == sv.end());
+
+  return true;
+}
+
+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};
+  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., there is no
+  // gap between the last stride and the end).
+  strider_iter--;
+  assert(*strider_iter == 1);
+
+  strider_iter++;
+  assert(*strider_iter == 8);
+
+  // By striding past the end, we are going to generate
+  // another gap between the last stride and the end.
+  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 gap gets used.
+  strider_iter += -2;
+  assert(*strider_iter == 1);
+
+  return true;
+}
+
+constexpr bool test() {
+  {
+    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+    test_forward_operator_increment(arr, arr + 11);
+  }
+  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));
+  }
+
+  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..77754e5181878
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+//  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>
+
+#include "../types.h"
+#include "test_macros.h"
+
+template <typename T>
+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*, /*IsSwappable=*/true, /*IsNoExcept=*/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);
+  }
+
+  {
+    // 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*, /*IsSwappable=*/true, /*IsNoExcept=*/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);
+  }
+
+  {
+    // 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, /*IsSwappable=*/true, /*IsNoExcept=*/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);
+  }
+
+  {
+    // Verify return type is a rvalue-reference.
+    int a[]    = {1, 2, 3};
+    using Base = BasicTestView<int*, int*>;
+    auto sv    = std::ranges::stride_view(Base(a, a + 3), 1);
+    auto it    = sv.begin();
+    static_assert(std::is_same_v<decltype(std::ranges::iter_move(it)), int&&>);
+    assert(std::ranges::iter_move(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/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..23846a1c2f9d3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 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"
+
+template <typename T>
+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*, /*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(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);
+  }
+
+  {
+    // 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*, /*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(!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);
+  }
+
+  {
+    // 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>>);
+  }
+
+  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/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
new file mode 100644
index 0000000000000..04e79f2b40cca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus.pass.cpp
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 
diff erence_type operator-(const iterator& x, const iterator& y)
+// friend constexpr 
diff erence_type operator-(default_sentinel_t, const iterator& x)
+// friend constexpr 
diff erence_type operator-(const iterator& x, default_sentinel_t)
+// friend constexpr iterator operator-(const iterator& i, 
diff erence_type s)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class T>
+concept CanMinus = std::is_same_v<typename T::
diff erence_type, decltype(std::declval<T>() - std::declval<T>())> &&
+                   requires(T& t) { t - t; };
+
+template <class T>
+concept CanSentinelMinus =
+    std::is_same_v<typename T::
diff erence_type, decltype(std::declval<T>() - std::default_sentinel)> &&
+    std::is_same_v<typename T::
diff erence_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 
diff erence 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 
diff erence 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 
diff erence 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 
diff erence 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 
diff erences (uses ceil for non-forward).
+  assert(mid0 - begin0 == 1);
+  assert(far0 - begin0 == 2);
+  assert(begin1 - begin0 == 1);
+  assert(mid1 - begin0 == 2);
+
+  // Negative 
diff erences.
+  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() {
+  {
+    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());
+  }
+  {
+    // operator-(iterator, 
diff erence_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);
+  }
+  {
+    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+    test_non_forward_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10));
+  }
+  {
+    // Test end - begin on the same view where size % stride != 0.
+    // 10 elements, stride 3: strided elements at 0,3,6,9.
+    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 b     = sv.begin();
+    auto e     = sv.end();
+    assert(e - b == 4);
+    assert(b - e == -4);
+    assert(b - b == 0);
+    assert(e - e == 0);
+  }
+  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/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..1f9c2b0d625f6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/minus_equal.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr iterator& operator-=(
diff erence_type n)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#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/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
new file mode 100644
index 0000000000000..221037a7173ab
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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+(
diff erence_type n, const iterator& i)
+// constexpr iterator operator+(const iterator& i, 
diff erence_type n)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#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::
diff erence_type>())> &&
+    std::is_same_v<T, decltype(std::declval<typename T::
diff erence_type>() + std::declval<T>())> &&
+    requires(T& t, typename T::
diff erence_type u) { t = t + u; } &&
+    requires(T& t, typename T::
diff erence_type u) { t = u + t; };
+
+// Make sure that we cannot use + on a stride view iterator and 
diff erence_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 
diff erence_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 
diff erence_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 
diff erence_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() {
+  std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+
+  auto begin    = vec.begin();
+  auto end      = vec.end();
+  auto distance = 4;
+
+  using Base = RandomAccessView<std::vector<int>::iterator>;
+  static_assert(std::ranges::random_access_range<Base>);
+
+  {
+    // 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;
+}
+
+int main(int, char**) {
+  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
new file mode 100644
index 0000000000000..ce8735e2bf839
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp
@@ -0,0 +1,134 @@
+//===----------------------------------------------------------------------===//
+//
+// 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+=(
diff erence_type n)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#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::
diff erence_type>())> &&
+                  requires(T& t, typename T::
diff erence_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() {
+  std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+
+  using Iter = std::vector<int>::iterator;
+
+  auto begin = vec.begin();
+  auto end   = vec.end();
+
+  using Base = RandomAccessView<Iter>;
+  static_assert(std::ranges::random_access_range<Base>);
+  auto base = Base(begin, end);
+
+  // += with stride 1: advancing by distance matches starting at begin + distance.
+  {
+    auto sv      = std::ranges::stride_view(base, 1);
+    auto it      = sv.begin();
+    auto& result = (it += 4);
+    assert(&result == &it);
+
+    auto it2 = std::ranges::stride_view(Base(begin + 4, end), 1).begin();
+    assert(*it == *it2);
+  }
+
+  // += past the end, then -= back: the remainder is handled correctly.
+  {
+    auto sv = std::ranges::stride_view(base, (end - begin) - 1);
+    auto it = sv.begin();
+
+    // 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.
+    it += 2;
+    it -= 1;
+    assert(*it == *(sv.begin() + 1));
+  }
+
+  // += 0 is a no-op.
+  {
+    auto sv = std::ranges::stride_view(base, 3);
+    auto it = sv.begin();
+    it += 1; // at index 3
+    assert(*it == *(begin + 3));
+    it += 0;
+    assert(*it == *(begin + 3));
+  }
+
+  // += with negative n.
+  {
+    auto sv = std::ranges::stride_view(base, 3);
+    auto it = sv.begin();
+    it += 3; // at index 9
+    assert(*it == *(begin + 9));
+    it += -2; // back to index 3
+    assert(*it == *(begin + 3));
+    it += -1; // back to index 0
+    assert(*it == *begin);
+  }
+
+  // += negative from end.
+  {
+    int arr[]        = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+    using CommonBase = BasicTestView<int*, int*>;
+    auto sv          = std::ranges::stride_view(CommonBase(arr, arr + 11), 3);
+    auto it          = sv.end();
+    it += -1; // last strided element (index 9)
+    assert(*it == 10);
+    it += -3; // back to begin (index 0)
+    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/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
new file mode 100644
index 0000000000000..c22d6afe4e467
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/subscript.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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[](
diff erence_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);
+  }
+  {
+    // Verify return type is a reference.
+    int arr[]  = {1, 2, 3};
+    using Base = BasicTestView<int*, int*>;
+    auto sv    = std::ranges::stride_view(Base(arr, arr + 3), 1);
+    auto it    = sv.begin();
+    static_assert(std::is_same_v<decltype(it[0]), 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/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1e07e86045b6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/types.compile.pass.cpp
@@ -0,0 +1,140 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// stride_view::iterator::
diff erence_type
+// stride_view::iterator::value_type
+// stride_view::iterator::iterator_concept
+// stride_view::iterator::iterator_category
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class T>
+concept HasIteratorCategory = requires { typename T::iterator_category; };
+
+template <class Iterator>
+using StrideViewFor = std::ranges::stride_view<BasicTestView<Iterator, sentinel_wrapper<Iterator>>>;
+
+template <class Iterator>
+using StrideIteratorFor = std::ranges::iterator_t<StrideViewFor<Iterator>>;
+
+struct ForwardIteratorWithInputCategory {
+  using 
diff erence_type   = int;
+  using value_type        = int;
+  using iterator_category = std::input_iterator_tag;
+  using iterator_concept  = std::forward_iterator_tag;
+  ForwardIteratorWithInputCategory();
+  ForwardIteratorWithInputCategory& operator++();
+  ForwardIteratorWithInputCategory operator++(int);
+  int& operator*() const;
+  friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory);
+};
+static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
+
+// Non-simple view: forward when non-const, raw pointer when const.
+// This exposes the bug where iterator_category is always derived from
+// the non-const view's iterator instead of maybe-const<Const, V>.
+struct DifferentCategoryView : std::ranges::view_base {
+  forward_iterator<int*> begin();
+  forward_iterator<int*> end();
+  const int* begin() const;
+  const int* end() const;
+};
+
+// Non-simple view: input-only when non-const, forward when const.
+// Tests that iterator_category is absent for the non-const iterator
+// but present for the const iterator.
+struct ForwardWhenConstView : std::ranges::view_base {
+  cpp17_input_iterator<int*> begin();
+  sentinel_wrapper<cpp17_input_iterator<int*>> end();
+  forward_iterator<const int*> begin() const;
+  forward_iterator<const int*> end() const;
+};
+
+void f() {
+  // Check that value_type is range_value_t and 
diff erence_type is range_
diff erence_t
+  {
+    auto check = []<class Iterator> {
+      using StrideView     = StrideViewFor<Iterator>;
+      using StrideIterator = StrideIteratorFor<Iterator>;
+      static_assert(std::is_same_v<typename StrideIterator::value_type, std::ranges::range_value_t<StrideView>>);
+      static_assert(
+          std::is_same_v<typename StrideIterator::
diff erence_type, std::ranges::range_
diff erence_t<StrideView>>);
+    };
+    check.operator()<cpp17_input_iterator<int*>>();
+    check.operator()<forward_iterator<int*>>();
+    check.operator()<bidirectional_iterator<int*>>();
+    check.operator()<random_access_iterator<int*>>();
+    check.operator()<contiguous_iterator<int*>>();
+    check.operator()<int*>();
+  }
+
+  // Check iterator_concept for various categories of ranges
+  {
+    static_assert(
+        std::is_same_v<StrideIteratorFor<cpp17_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<StrideIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<ForwardIteratorWithInputCategory>::iterator_concept,
+                                 std::forward_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<bidirectional_iterator<int*>>::iterator_concept,
+                                 std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<random_access_iterator<int*>>::iterator_concept,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<contiguous_iterator<int*>>::iterator_concept,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<int*>::iterator_concept, std::random_access_iterator_tag>);
+  }
+
+  // Check iterator_category for various categories of ranges
+  {
+    static_assert(!HasIteratorCategory<StrideIteratorFor<cpp17_input_iterator<int*>>>);
+    static_assert(
+        std::is_same_v<StrideIteratorFor<forward_iterator<int*>>::iterator_category, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<ForwardIteratorWithInputCategory>::iterator_category,
+                                 std::input_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<bidirectional_iterator<int*>>::iterator_category,
+                                 std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<random_access_iterator<int*>>::iterator_category,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<contiguous_iterator<int*>>::iterator_category,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<StrideIteratorFor<int*>::iterator_category, std::random_access_iterator_tag>);
+  }
+
+  // Check that const vs non-const iterators get the correct iterator_category
+  // when the view has 
diff erent iterator categories for const and non-const.
+  {
+    using SV           = std::ranges::stride_view<DifferentCategoryView>;
+    using NonConstIter = std::ranges::iterator_t<SV>;
+    using ConstIter    = std::ranges::iterator_t<const SV>;
+
+    static_assert(std::is_same_v<NonConstIter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::iterator_concept, std::random_access_iterator_tag>);
+
+    static_assert(std::is_same_v<NonConstIter::iterator_category, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::iterator_category, std::random_access_iterator_tag>);
+  }
+
+  // Check that iterator_category presence/absence depends on the correct
+  // const-qualified Base, not always the non-const view.
+  {
+    using SV           = std::ranges::stride_view<ForwardWhenConstView>;
+    using NonConstIter = std::ranges::iterator_t<SV>;
+    using ConstIter    = std::ranges::iterator_t<const SV>;
+
+    static_assert(!HasIteratorCategory<NonConstIter>);
+    static_assert(HasIteratorCategory<ConstIter>);
+    static_assert(std::is_same_v<ConstIter::iterator_category, std::forward_iterator_tag>);
+  }
+}

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..f5c788c26ce6a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 auto size() requires sized_range<V>
+// constexpr auto size() const requires sized_range<const V>
+
+#include <cassert>
+#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*>, /*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.
+    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.
+    auto iota = std::views::iota(0, 22);
+    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() == 8);
+  }
+  {
+    // Empty range.
+    auto strided = std::views::iota(0, 0) | std::views::stride(3);
+    assert(strided.size() == 0);
+  }
+  {
+    // Stride larger than range size.
+    auto strided = std::views::iota(0, 3) | std::views::stride(10);
+    assert(strided.size() == 1);
+  }
+  {
+    // Stride equal to range size.
+    auto strided = std::views::iota(0, 5) | std::views::stride(5);
+    assert(strided.size() == 1);
+  }
+  {
+    // Stride of 1.
+    auto strided = std::views::iota(0, 7) | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+  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..6ec8b834eb9b7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 range_
diff erence_t<V> stride() const noexcept;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "types.h"
+
+constexpr bool test() {
+  using View = BasicTestView<cpp17_input_iterator<int*>>;
+  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_
diff erence_t<View>, decltype(strided.stride()));
+  assert(strided.stride() == 2);
+
+  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..5a8d6b6ca2f71
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -0,0 +1,290 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <concepts>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <utility>
+
+#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 
diff erence_type = typename std::iterator_traits<Iter>::
diff erence_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::
diff erence_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 
diff erence_type operator-(const Derived& left, const Derived& right)
+    requires IsSized
+  {
+    return left.value_ - right.value_;
+  }
+};
+
+// 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>;
+
+// 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 8f421e8cbd0f0..7bb78cc96b4ee 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1173,6 +1173,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},


        


More information about the libcxx-commits mailing list