[libcxx-commits] [libcxx] 1c51886 - [libc++] Implement P3168R2: Give optional range support (#149441)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Aug 18 08:04:48 PDT 2025
Author: William Tran-Viet
Date: 2025-08-18T18:04:45+03:00
New Revision: 1c5188692036c51123ae78e9208d5a375d28f74a
URL: https://github.com/llvm/llvm-project/commit/1c5188692036c51123ae78e9208d5a375d28f74a
DIFF: https://github.com/llvm/llvm-project/commit/1c5188692036c51123ae78e9208d5a375d28f74a.diff
LOG: [libc++] Implement P3168R2: Give optional range support (#149441)
Resolves #105430
- Implement all required pieces of P3168R2
- Leverage existing `wrap_iter` and `bounded_iter` classes to implement
the `optional` regular and hardened iterator type, respectively
- Update documentation to match
Added:
libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
Modified:
libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/ReleaseNotes/22.rst
libcxx/docs/Status/Cxx2cPapers.csv
libcxx/include/__iterator/wrap_iter.h
libcxx/include/optional
libcxx/include/version
libcxx/modules/std/optional.inc
libcxx/test/std/language.support/support.limits/support.limits.general/optional.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/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
index 699d3f8866861..d4ce32ce5b17f 100644
--- a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
+++ b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
@@ -5,5 +5,6 @@ set(_defines
_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
_LIBCPP_ABI_BOUNDED_UNIQUE_PTR
_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY
+ _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
)
set(LIBCXX_ABI_DEFINES "${_defines}" CACHE STRING "")
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index a36848ebd24b4..358889d8dbc37 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_not_fn`` ``202306L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_optional_range_support`` *unimplemented*
+ ``__cpp_lib_optional_range_support`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` ``202311L``
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 191dab6b77564..f28babf548fe4 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -39,6 +39,7 @@ Implemented Papers
------------------
- P2321R2: ``zip`` (`Github <https://github.com/llvm/llvm-project/issues/105169>`__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release)
+- P3168R2: Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index e8b0c9559f40b..3b8b2b7ad0b3f 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -66,7 +66,7 @@
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
-"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","",""
+"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22",""
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 2b5bc489dd44c..7610586ddecbb 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -117,6 +117,8 @@ class __wrap_iter {
friend class span;
template <class _Tp, size_t _Size>
friend struct array;
+ template <class _Tp>
+ friend class optional;
};
template <class _Iter1>
diff --git a/libcxx/include/optional b/libcxx/include/optional
index e81bff50daad6..39fcaa2c2ec18 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -20,6 +20,11 @@ namespace std {
template <class T>
class optional;
+ template<class T>
+ constexpr bool ranges::enable_view<optional<T>> = true;
+ template<class T>
+ constexpr auto format_kind<optional<T>> = range_format::disabled;
+
template<class T>
concept is-derived-from-optional = requires(const T& t) { // exposition only
[]<class U>(const optional<U>&){ }(t);
@@ -102,6 +107,8 @@ namespace std {
class optional {
public:
using value_type = T;
+ using iterator = implementation-defined; // see [optional.iterators]
+ using const_iterator = implementation-defined; // see [optional.iterators]
// [optional.ctor], constructors
constexpr optional() noexcept;
@@ -135,6 +142,12 @@ namespace std {
// [optional.swap], swap
void swap(optional &) noexcept(see below ); // constexpr in C++20
+ // [optional.iterators], iterator support
+ constexpr iterator begin() noexcept;
+ constexpr const_iterator begin() const noexcept;
+ constexpr iterator end() noexcept;
+ constexpr const_iterator end() const noexcept;
+
// [optional.observe], observers
constexpr T const *operator->() const noexcept;
constexpr T *operator->() noexcept;
@@ -186,13 +199,18 @@ namespace std {
# include <__compare/three_way_comparable.h>
# include <__concepts/invocable.h>
# include <__config>
+# include <__cstddef/ptr
diff _t.h>
# include <__exception/exception.h>
+# include <__format/range_format.h>
# include <__functional/hash.h>
# include <__functional/invoke.h>
# include <__functional/unary_function.h>
# include <__fwd/functional.h>
+# include <__iterator/bounded_iter.h>
+# include <__iterator/wrap_iter.h>
# include <__memory/addressof.h>
# include <__memory/construct_at.h>
+# include <__ranges/enable_view.h>
# include <__tuple/sfinae_helpers.h>
# include <__type_traits/add_pointer.h>
# include <__type_traits/conditional.h>
@@ -207,6 +225,7 @@ namespace std {
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_core_convertible.h>
# include <__type_traits/is_destructible.h>
+# include <__type_traits/is_function.h>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_object.h>
@@ -219,6 +238,7 @@ namespace std {
# include <__type_traits/is_trivially_constructible.h>
# include <__type_traits/is_trivially_destructible.h>
# include <__type_traits/is_trivially_relocatable.h>
+# include <__type_traits/is_unbounded_array.h>
# include <__type_traits/negation.h>
# include <__type_traits/remove_const.h>
# include <__type_traits/remove_cv.h>
@@ -567,6 +587,14 @@ using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
template <class _Tp>
class optional;
+# if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+# endif
+
# if _LIBCPP_STD_VER >= 20
template <class _Tp>
@@ -586,9 +614,21 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
private __optional_sfinae_assign_base_t<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
+ using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>;
+ using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
+
public:
using value_type = _Tp;
+# if _LIBCPP_STD_VER >= 26
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ using iterator = __bounded_iter<__wrap_iter<__pointer>>;
+ using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
+# else
+ using iterator = __wrap_iter<__pointer>;
+ using const_iterator = __wrap_iter<__const_pointer>;
+# endif
+# endif
using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>;
@@ -792,6 +832,34 @@ public:
}
}
+# if _LIBCPP_STD_VER >= 26
+ // [optional.iterators], iterator support
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ return std::__make_bounded_iter(
+ std::__wrap_iter<__pointer>(std::addressof(this->__get())),
+ std::__wrap_iter<__pointer>(std::addressof(this->__get())),
+ std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
+# else
+ return iterator(std::addressof(this->__get()));
+# endif
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ return std::__make_bounded_iter(
+ std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
+ std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
+ std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
+# else
+ return const_iterator(std::addressof(this->__get()));
+# endif
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
+ _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); }
+# endif
+
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
return std::addressof(this->__get());
diff --git a/libcxx/include/version b/libcxx/include/version
index aae9277a7dfc6..16917a3bd9ddd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -585,7 +585,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_mdspan 202406L
# undef __cpp_lib_not_fn
# define __cpp_lib_not_fn 202306L
-// # define __cpp_lib_optional_range_support 202406L
+# define __cpp_lib_optional_range_support 202406L
# undef __cpp_lib_out_ptr
# define __cpp_lib_out_ptr 202311L
// # define __cpp_lib_philox_engine 202406L
diff --git a/libcxx/modules/std/optional.inc b/libcxx/modules/std/optional.inc
index 0f812bc0e24a4..9ee51117277ce 100644
--- a/libcxx/modules/std/optional.inc
+++ b/libcxx/modules/std/optional.inc
@@ -10,7 +10,12 @@
export namespace std {
// [optional.optional], class template optional
using std::optional;
-
+#if _LIBCPP_STD_VER >= 26
+ // [optional.iterators], iterator support
+ namespace ranges {
+ using std::ranges::enable_view;
+ }
+#endif
// [optional.nullopt], no-value state indicator
using std::nullopt;
using std::nullopt_t;
@@ -18,6 +23,10 @@ export namespace std {
// [optional.bad.access], class bad_optional_access
using std::bad_optional_access;
+#if _LIBCPP_STD_VER >= 26
+ using std::format_kind;
+#endif
+
// [optional.relops], relational operators
using std::operator==;
using std::operator!=;
diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..3cdd7553e2e5d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// template <class T> class optional::iterator;
+// template <class T> class optional::const_iterator;
+
+#include <optional>
+
+template <typename T>
+concept has_iterator_aliases = requires {
+ typename T::iterator;
+ typename T::const_iterator;
+};
+
+static_assert(has_iterator_aliases<std::optional<int>>);
+static_assert(has_iterator_aliases<std::optional<const int>>);
+
+// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>
+
+// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
+// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp
index ccdb1a8c11a0b..aca6290f5a4bf 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp
@@ -146,17 +146,11 @@
# error "__cpp_lib_optional should have the value 202110L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++26"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++26"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
# endif
#endif // TEST_STD_VER > 23
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 7bd8e8979e6f3..cde2f258b7732 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
@@ -7437,17 +7437,11 @@
# error "__cpp_lib_optional should have the value 202110L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++26"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++26"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_out_ptr
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
new file mode 100644
index 0000000000000..df95a8df3793f
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// constexpr iterator optional::begin() noexcept;
+// constexpr const_iterator optional::begin() const noexcept;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+template <typename T>
+constexpr bool test() {
+ std::optional<T> opt{T{}};
+
+ { // begin() is marked noexcept
+ static_assert(noexcept(opt.begin()));
+ static_assert(noexcept(std::as_const(opt).begin()));
+ }
+
+ { // Dereferencing an iterator at the beginning == indexing the 0th element, and that calling begin() again return the same iterator.
+ auto iter1 = opt.begin();
+ auto iter2 = std::as_const(opt).begin();
+ assert(*iter1 == iter1[0]);
+ assert(*iter2 == iter2[0]);
+ assert(iter1 == opt.begin());
+ assert(iter2 == std::as_const(opt).begin());
+ }
+
+ { // Calling begin() multiple times on a disengaged optional returns the same iterator.
+ std::optional<T> disengaged{std::nullopt};
+ auto iter1 = disengaged.begin();
+ auto iter2 = std::as_const(disengaged).begin();
+ assert(iter1 == disengaged.begin());
+ assert(iter2 == std::as_const(disengaged).begin());
+ }
+
+ return true;
+}
+
+constexpr bool tests() {
+ assert(test<int>());
+ assert(test<char>());
+ assert(test<const int>());
+ assert(test<const char>());
+ return true;
+}
+
+int main(int, char**) {
+ assert(tests());
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
new file mode 100644
index 0000000000000..966c3e7441880
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// constexpr iterator optional::end() noexcept;
+// constexpr const_iterator optional::end() const noexcept;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <utility>
+
+template <typename T>
+constexpr bool test() {
+ std::optional<T> disengaged{std::nullopt};
+
+ { // end() is marked noexcept
+ static_assert(noexcept(disengaged.end()));
+ static_assert(noexcept(std::as_const(disengaged).end()));
+ }
+
+ { // end() == begin() and end() == end() if the optional is disengaged
+ auto it = disengaged.end();
+ auto it2 = std::as_const(disengaged).end();
+
+ assert(it == disengaged.begin());
+ assert(disengaged.begin() == it);
+ assert(it == disengaged.end());
+
+ assert(it2 == std::as_const(disengaged).begin());
+ assert(std::as_const(disengaged).begin() == it2);
+ assert(it2 == std::as_const(disengaged).end());
+ }
+
+ std::optional<T> engaged{T{}};
+
+ { // end() != begin() if the optional is engaged
+ auto it = engaged.end();
+ auto it2 = std::as_const(engaged).end();
+
+ assert(it != engaged.begin());
+ assert(engaged.begin() != it);
+
+ assert(it2 != std::as_const(engaged).begin());
+ assert(std::as_const(engaged).begin() != it2);
+ }
+
+ return true;
+}
+
+constexpr bool tests() {
+ assert(test<int>());
+ assert(test<char>());
+ assert(test<const int>());
+ assert(test<const char>());
+
+ return true;
+}
+
+int main(int, char**) {
+ assert(tests());
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
new file mode 100644
index 0000000000000..1203290a0290a
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// template <class T> class optional::iterator;
+// template <class T> class optional::const_iterator;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+template <typename T, T __val>
+constexpr bool test() {
+ std::optional<T> opt{__val};
+
+ { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds.
+ auto it = opt.begin();
+ auto it2 = std::as_const(opt).begin();
+ assert(*it == *opt);
+ assert(*it2 == *std::as_const(opt));
+ }
+
+ { // optional::iterator and optional::const_iterator satisfy the Cpp17RandomAccessIterator and contiguous iterator.
+ auto it = opt.begin();
+ auto it2 = std::as_const(opt).begin();
+ assert(std::contiguous_iterator<decltype(it)>);
+ assert(std::contiguous_iterator<decltype(it2)>);
+
+ assert(std::random_access_iterator<decltype(it)>);
+ assert(std::random_access_iterator<decltype(it2)>);
+ }
+
+ { // const_iterator::value_type == std::remove_cv_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t<T>, iterator::reference == T&
+ auto it = opt.begin();
+ auto it2 = std::as_const(opt).begin();
+ assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cv_t<T>>));
+ assert((std::is_same_v<typename decltype(it)::reference, T&>));
+ assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cv_t<T>>));
+ assert((std::is_same_v<typename decltype(it2)::reference, const T&>));
+ }
+
+ { // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 0
+ const std::optional<T> disengaged{std::nullopt};
+ std::optional<T> disengaged2{std::nullopt};
+ assert(std::ranges::size(opt) == 1);
+ assert(std::ranges::size(std::as_const(opt)) == 1);
+
+ assert(std::ranges::size(disengaged) == 0);
+ assert(std::ranges::size(disengaged2) == 0);
+ }
+
+ { // std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
+ static_assert(std::ranges::enable_view<std::optional<T>> == true);
+ static_assert(std::format_kind<std::optional<T>> == std::range_format::disabled);
+ }
+
+ // An optional with value that is reset will have a begin() == end(), then when it is reassigned a value,
+ // begin() != end(), and *begin() will contain the new value.
+ {
+ std::optional<T> val{__val};
+ assert(val.begin() != val.end());
+ val.reset();
+ assert(val.begin() == val.end());
+ val.emplace(__val);
+ assert(val.begin() != val.end());
+ assert(*(val.begin()) == __val);
+ }
+
+ return true;
+}
+
+constexpr bool tests() {
+ assert((test<int, 1>()));
+ assert((test<char, 'a'>()));
+ assert((test<bool, true>()));
+ assert((test<const int, 2>()));
+ assert((test<const char, 'b'>()));
+
+ return true;
+}
+
+int main(int, char**) {
+ assert(tests());
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d9317e00e3f4a..8d57a07b8836b 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1012,7 +1012,6 @@ def add_version_header(tc):
"name": "__cpp_lib_optional_range_support",
"values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support
"headers": ["optional"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_out_ptr",
More information about the libcxx-commits
mailing list