[libcxx-commits] [libcxx] WIP [libc++][ranges] P3052R2: `view_interface::at()` (PR #205012)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jun 21 13:53:21 PDT 2026


https://github.com/H-G-Hristov created https://github.com/llvm/llvm-project/pull/205012

Closes #204395

Implements https://wg21.link/P3052R2

>From ad563100c32b586bf00c223a155ec233ad93278e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Jun 2026 23:52:40 +0300
Subject: [PATCH] [libc++][ranges] P3052R2: `view_interface::at()`

Closes #204395

Implements https://wg21.link/P3052R2
---
 libcxx/include/__ranges/view_interface.h      | 22 +++++++++++++++++++
 .../view.interface/nodiscard.verify.cpp       |  5 +++++
 .../view.interface/view.interface.pass.cpp    | 16 ++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 37b2c9e2c1a75..efe263e895d16 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/prev.h>
 #include <__memory/pointer_traits.h>
@@ -25,6 +26,7 @@
 #include <__type_traits/is_class.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <stdexcept>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -159,6 +161,26 @@ class view_interface {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const {
     return ranges::begin(__derived())[__index];
   }
+
+  template <random_access_range _RARange = _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+  template <random_access_range _RARange = const _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
 };
 
 } // namespace ranges
diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
index a22c03f5d1a71..5d076211bd5b3 100644
--- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
@@ -67,4 +67,9 @@ void test() {
   v[Diff{0}];
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v)[Diff{0}];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.at(Diff{0});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).at(Diff{0});
 }
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..c170613e582ee 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -300,6 +300,22 @@ constexpr bool testSubscript() {
   return true;
 }
 
+template<class T>
+concept AtInvocable = requires (T const& obj, std::size_t n) { obj.at(n); };
+
+constexpr bool testAt() {
+  static_assert(!AtInvocable<ForwardRange>);
+  static_assert( AtInvocable<RARange>);
+
+  RARange randomAccess;
+  assert(randomAccess.at(2) == 2);
+  assert(static_cast<RARange const&>(randomAccess).at(2) == 2);
+  randomAccess.at(2) = 3;
+  assert(randomAccess.at(2) == 3);
+
+  return true;
+}
+
 template<class T>
 concept FrontInvocable = requires (T const& obj) { obj.front(); };
 



More information about the libcxx-commits mailing list