[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
This commit is contained in:
parent
4ab14685a0
commit
1c51886920
@ -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 "")
|
||||
|
@ -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``
|
||||
---------------------------------------------------------- -----------------
|
||||
|
@ -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
|
||||
-----------------------------
|
||||
|
@ -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)","","",""
|
||||
|
|
@ -117,6 +117,8 @@ private:
|
||||
friend class span;
|
||||
template <class _Tp, size_t _Size>
|
||||
friend struct array;
|
||||
template <class _Tp>
|
||||
friend class optional;
|
||||
};
|
||||
|
||||
template <class _Iter1>
|
||||
|
@ -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/ptrdiff_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());
|
||||
|
@ -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
|
||||
|
@ -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!=;
|
||||
|
@ -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)>>);
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1012,7 +1012,6 @@ feature_test_macros = [
|
||||
"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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user