[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 22:41:04 PDT 2026
https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/205012
>From f00334e70d36c9fae3640d0bec848f81732348ad 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/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/Status/Cxx29Papers.csv | 2 +-
libcxx/include/__ranges/view_interface.h | 22 +++
libcxx/include/version | 2 +
.../view.interface/nodiscard.verify.cpp | 5 +
libcxx/test/libcxx/transitive_includes.gen.py | 2 +-
.../test/libcxx/transitive_includes/cxx23.csv | 1 +
.../test/libcxx/transitive_includes/cxx26.csv | 1 +
.../ranges.version.compile.pass.cpp | 33 ++++
.../version.version.compile.pass.cpp | 33 ++++
.../view.interface/view.interface.pass.cpp | 145 ++++++++++++------
.../generate_feature_test_macro_components.py | 7 +
12 files changed, 205 insertions(+), 50 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..e27b9433baeaa 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -308,6 +308,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_variant`` ``202106L``
---------------------------------------------------------- -----------------
+ ``__cpp_lib_view_interface`` ``202606L``
+ ---------------------------------------------------------- -----------------
**C++23**
----------------------------------------------------------------------------
``__cpp_lib_adaptor_iterator_pair_constructor`` ``202106L``
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index a5d896f260657..733eab1a3b29e 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -4,7 +4,7 @@
"`P2414R12 <https://wg21.link/P2414R12>`__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 <https://github.com/llvm/llvm-project/issues/204392>`__","Voted as a Defect Report."
"`P3319R6 <https://wg21.link/P3319R6>`__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 <https://github.com/llvm/llvm-project/issues/204393>`__",""
"`P3798R1 <https://wg21.link/P3798R1>`__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 <https://github.com/llvm/llvm-project/issues/204394>`__",""
-"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
+"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","|Complete|,"23","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
"`P4206R0 <https://wg21.link/P4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","","","`#203336 <https://github.com/llvm/llvm-project/issues/203336>`__","To be applied as a Defect Report."
"`P3395R6 <https://wg21.link/P3395R6>`__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 <https://github.com/llvm/llvm-project/issues/204396>`__",""
"`P3505R4 <https://wg21.link/P3505R4>`__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 <https://github.com/llvm/llvm-project/issues/204397>`__","To be applied as a Defect Report."
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/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..744eafd35b72a 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -289,6 +289,7 @@ __cpp_lib_unwrap_ref 201811L <functional>
__cpp_lib_variant 202306L <variant>
202106L // C++20
202102L // C++17
+__cpp_lib_view_interface 202606L <ranges>
__cpp_lib_void_t 201411L <type_traits>
*/
@@ -487,6 +488,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_unwrap_ref 201811L
# undef __cpp_lib_variant
# define __cpp_lib_variant 202106L
+# define __cpp_lib_view_interface 202606L
#endif
#if _LIBCPP_STD_VER >= 23
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/libcxx/transitive_includes.gen.py b/libcxx/test/libcxx/transitive_includes.gen.py
index 198a155ac0ee5..b697758575b80 100644
--- a/libcxx/test/libcxx/transitive_includes.gen.py
+++ b/libcxx/test/libcxx/transitive_includes.gen.py
@@ -54,7 +54,7 @@
# To re-generate the list of expected headers, temporarily set this to True, and run this test.
# Note that this needs to be done for all supported language versions of libc++:
# for std in c++03 c++11 c++14 c++17 c++20 c++23 c++26; do <build>/bin/llvm-lit --param std=$std libcxx/test/libcxx/transitive_includes.gen.py; done
-regenerate_expected_results = False
+regenerate_expected_results = True
if regenerate_expected_results:
print(
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..f5a2c2451fcf9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
algorithm limits
algorithm optional
algorithm ratio
+algorithm stdexcept
algorithm tuple
algorithm version
any cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 1450a13e125ef..f5c27ff707840 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
algorithm limits
algorithm optional
algorithm ratio
+algorithm stdexcept
algorithm tuple
algorithm version
any cstdint
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 670b0e664721a..f42c7ede894b5 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
@@ -80,6 +80,10 @@
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
#elif TEST_STD_VER == 14
# ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -142,6 +146,10 @@
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
#elif TEST_STD_VER == 17
# ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -204,6 +212,10 @@
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
#elif TEST_STD_VER == 20
# ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -269,6 +281,13 @@
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++20"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++20"
+# endif
+
#elif TEST_STD_VER == 23
# ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -385,6 +404,13 @@
# error "__cpp_lib_ranges_zip should have the value 202110L in c++23"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++23"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++23"
+# endif
+
#elif TEST_STD_VER > 23
# if !defined(_LIBCPP_VERSION)
@@ -516,6 +542,13 @@
# error "__cpp_lib_ranges_zip should have the value 202110L in c++26"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++26"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++26"
+# endif
+
#endif // TEST_STD_VER > 23
// clang-format on
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 d9c78b73f7e23..5328d43d32cc8 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
@@ -944,6 +944,10 @@
# error "__cpp_lib_variant should not be defined before c++17"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
# ifdef __cpp_lib_void_t
# error "__cpp_lib_void_t should not be defined before c++17"
# endif
@@ -1946,6 +1950,10 @@
# error "__cpp_lib_variant should not be defined before c++17"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
# ifdef __cpp_lib_void_t
# error "__cpp_lib_void_t should not be defined before c++17"
# endif
@@ -3137,6 +3145,10 @@
# error "__cpp_lib_variant should have the value 202102L in c++17"
# endif
+# ifdef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should not be defined before c++20"
+# endif
+
# ifndef __cpp_lib_void_t
# error "__cpp_lib_void_t should be defined in c++17"
# endif
@@ -4595,6 +4607,13 @@
# error "__cpp_lib_variant should have the value 202106L in c++20"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++20"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++20"
+# endif
+
# ifndef __cpp_lib_void_t
# error "__cpp_lib_void_t should be defined in c++20"
# endif
@@ -6281,6 +6300,13 @@
# error "__cpp_lib_variant should have the value 202106L in c++23"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++23"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++23"
+# endif
+
# ifndef __cpp_lib_void_t
# error "__cpp_lib_void_t should be defined in c++23"
# endif
@@ -8303,6 +8329,13 @@
# error "__cpp_lib_variant should have the value 202306L in c++26"
# endif
+# ifndef __cpp_lib_view_interface
+# error "__cpp_lib_view_interface should be defined in c++26"
+# endif
+# if __cpp_lib_view_interface != 202606L
+# error "__cpp_lib_view_interface should have the value 202606L in c++26"
+# endif
+
# ifndef __cpp_lib_void_t
# error "__cpp_lib_void_t should be defined in c++26"
# endif
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..23512ccc15517 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
@@ -21,17 +21,17 @@
#include "test_macros.h"
#include "test_iterators.h"
-template<class T>
+template <class T>
concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
-struct Empty { };
+struct Empty {};
static_assert(!ValidViewInterfaceType<void>);
static_assert(!ValidViewInterfaceType<void*>);
static_assert(!ValidViewInterfaceType<Empty*>);
static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
using InputIter = cpp20_input_iterator<const int*>;
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
static_assert(std::ranges::sized_range<SizedInputRange>);
struct NotSizedSentinel {
- using value_type = int;
- using difference_type = std::ptrdiff_t;
+ using value_type = int;
+ using difference_type = std::ptrdiff_t;
using iterator_concept = std::forward_iterator_tag;
explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
using ForwardIter = forward_iterator<int*>;
// So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
- return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
struct ForwardRange : std::ranges::view_interface<ForwardRange> {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
static_assert(std::ranges::view<ForwardRange>);
struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
- int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
- MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
- MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
- MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
- MoveOnlyForwardRange() = default;
+ int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
+ MoveOnlyForwardRange(MoveOnlyForwardRange&&) = default;
+ MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+ MoveOnlyForwardRange() = default;
constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
};
static_assert(std::ranges::view<MoveOnlyForwardRange>);
-struct MI : std::ranges::view_interface<InputRange>,
- std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
static_assert(!std::ranges::view<MI>);
struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
constexpr ContIter begin() const { return ContIter(buff); }
constexpr ContIter end() const { return ContIter(buff + 8); }
- constexpr const int *data() const { return nullptr; }
+ constexpr const int* data() const { return nullptr; }
};
static_assert(std::ranges::view<DataIsNull>);
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
};
struct SentinelType {
- int *base_;
+ int* base_;
explicit SentinelType() = default;
- constexpr explicit SentinelType(int *base) : base_(base) {}
- friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
- friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
- friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
- friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+ constexpr explicit SentinelType(int* base) : base_(base) {}
+ friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+ return {base(iter) == sent.base_};
+ }
+ friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+ return {base(iter) == sent.base_};
+ }
+ friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+ return {base(iter) != sent.base_};
+ }
+ friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+ return {base(iter) != sent.base_};
+ }
};
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
};
static_assert(std::ranges::view<BoolConvertibleComparison>);
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
constexpr bool testEmpty() {
static_assert(!EmptyInvocable<InputRange>);
// LWG 3715: `view_interface::empty` is overconstrained
static_assert(EmptyInvocable<SizedInputRange>);
- static_assert( EmptyInvocable<ForwardRange>);
+ static_assert(EmptyInvocable<ForwardRange>);
static_assert(!BoolOpInvocable<InputRange>);
static_assert(BoolOpInvocable<SizedInputRange>);
- static_assert( BoolOpInvocable<ForwardRange>);
+ static_assert(BoolOpInvocable<ForwardRange>);
SizedInputRange sizedInputRange;
assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
return true;
}
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
constexpr bool testData() {
static_assert(!DataInvocable<ForwardRange>);
- static_assert( DataInvocable<ContRange>);
+ static_assert(DataInvocable<ContRange>);
ContRange contiguous;
assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
return true;
}
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
constexpr bool testSize() {
static_assert(!SizeInvocable<InputRange>);
static_assert(!SizeInvocable<NotSizedSentinel>);
- static_assert( SizeInvocable<ForwardRange>);
+ static_assert(SizeInvocable<ForwardRange>);
// Test the test.
static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
- using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+ using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
ForwardRange forwardRange;
assert(forwardRange.size() == 8);
assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
return true;
}
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
constexpr bool testSubscript() {
static_assert(!SubscriptInvocable<ForwardRange>);
- static_assert( SubscriptInvocable<RARange>);
+ static_assert(SubscriptInvocable<RARange>);
RARange randomAccess;
assert(randomAccess[2] == 2);
@@ -300,17 +304,57 @@ constexpr bool testSubscript() {
return true;
}
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+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);
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ if (!std::is_constant_evaluated()) {
+ try {
+ TEST_IGNORE_NODISCARD randomAccess.at(0);
+ assert(true);
+ } catch (std::out_of_range const&) {
+ // pass
+ assert(false); // This should not be reached because index 0 is valid.
+ } catch (...) {
+ assert(false);
+ }
+
+ // Test that at() throws an exception when the index is out of range.
+ try {
+ TEST_IGNORE_NODISCARD randomAccess.at(10);
+ assert(false);
+ } catch (std::out_of_range const&) {
+ // pass
+ } catch (...) {
+ assert(false);
+ }
+ }
+#endif
+
+ return true;
+}
+
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
constexpr bool testFrontBack() {
static_assert(!FrontInvocable<InputRange>);
- static_assert( FrontInvocable<ForwardRange>);
+ static_assert(FrontInvocable<ForwardRange>);
static_assert(!BackInvocable<ForwardRange>);
- static_assert( BackInvocable<RARange>);
+ static_assert(BackInvocable<RARange>);
ForwardRange forwardRange;
assert(forwardRange.front() == 0);
@@ -332,8 +376,10 @@ constexpr bool testFrontBack() {
return true;
}
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+ V1 base_;
+};
static_assert(sizeof(V2) == sizeof(V1));
int main(int, char**) {
@@ -349,6 +395,9 @@ int main(int, char**) {
testSubscript();
static_assert(testSubscript());
+ testAt();
+ static_assert(testAt());
+
testFrontBack();
static_assert(testFrontBack());
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..3934e101073a0 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1522,6 +1522,13 @@ def add_version_header(tc):
},
"headers": ["variant"],
},
+ {
+ "name": "__cpp_lib_view_interface",
+ "values": {
+ "c++20": 202606,
+ },
+ "headers": ["ranges"],
+ },
{
"name": "__cpp_lib_void_t",
"values": {"c++17": 201411},
More information about the libcxx-commits
mailing list