-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[libc++] Implement P3168R2: Give optional range support #149441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ac4dcbc
3185aa3
60f67b2
a7740e8
02cdb07
913229a
559825b
3baa163
e3398ae
3b015e7
c8318f1
ebdc19d
60bf66b
3683469
e9ec733
97f3a7b
7cab021
c0ff08a
718af43
5c60392
d81e017
10a2271
65b53af
1d9ac9a
d04201c
e9d0fa7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
|
@@ -579,16 +607,49 @@ struct __is_std_optional : false_type {}; | |
template <class _Tp> | ||
struct __is_std_optional<optional<_Tp>> : true_type {}; | ||
|
||
template <class _Tp, class = void> | ||
struct __optional_iterator_aliases {}; | ||
|
||
# if _LIBCPP_STD_VER >= 26 | ||
// disallow T (&)() and T (&)[] | ||
template <class _Tp> | ||
struct __optional_iterator_aliases< | ||
_Tp, | ||
__enable_if_t<!(is_reference<_Tp>::value && (is_function<__libcpp_remove_reference_t<_Tp>>::value || | ||
is_unbounded_array<__libcpp_remove_reference_t<_Tp>>::value))> > { | ||
Comment on lines
+618
to
+619
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it even possible to instantiate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not at the moment, since those would fall under There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO we should tackle it then. It's not clear to me that we even want to allow these kinds of instantiations, and I'm very much not a fan of working around problems that don't even exist yet. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not against it, but I'll ask @ldionne and @frederick-vs-ja what they think about cutting this out for a later patch. |
||
using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; | ||
using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>; | ||
# 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 | ||
|
||
template <class _Tp> | ||
class _LIBCPP_DECLSPEC_EMPTY_BASES optional | ||
: private __optional_move_assign_base<_Tp>, | ||
private __optional_sfinae_ctor_base_t<_Tp>, | ||
private __optional_sfinae_assign_base_t<_Tp> { | ||
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; | ||
private __optional_sfinae_assign_base_t<_Tp>, | ||
public __optional_iterator_aliases<_Tp> { | ||
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; | ||
using __iter_aliases _LIBCPP_NODEBUG = __optional_iterator_aliases<_Tp>; | ||
# if _LIBCPP_STD_VER >= 26 | ||
using typename __iter_aliases::__const_pointer; | ||
using typename __iter_aliases::__pointer; | ||
# endif | ||
|
||
public: | ||
using value_type = _Tp; | ||
|
||
# if _LIBCPP_STD_VER >= 26 | ||
using typename __iter_aliases::const_iterator; | ||
using typename __iter_aliases::iterator; | ||
# 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 +853,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()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)>>); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.