From: Patrick Palka <ppalka@redhat.com>
To: gcc-patches@gcc.gnu.org
Cc: libstdc++@gcc.gnu.org, Patrick Palka <ppalka@redhat.com>
Subject: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
Date: Fri, 14 Apr 2023 00:00:42 -0400 [thread overview]
Message-ID: <20230414040042.1498825-2-ppalka@redhat.com> (raw)
In-Reply-To: <20230414040042.1498825-1-ppalka@redhat.com>
This also implements the approved follow-up LWG issues 3765, 3766, 3769,
3770, 3811, 3850, 3853, 3862 and 3872.
Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
libstdc++-v3/ChangeLog:
* include/bits/ranges_base.h (const_iterator_t): Define for C++23.
(const_sentinel_t): Likewise.
(range_const_reference_t): Likewise.
(constant_range): Likewise.
(__cust_access::__possibly_const_range): Likewise, replacing ...
(__cust_access::__as_const): ... this.
(__cust_access::_CBegin::operator()): Redefine for C++23 as per P2278R4.
(__cust_access::_CEnd::operator()): Likewise.
(__cust_access::_CRBegin::operator()): Likewise.
(__cust_access::_CREnd::operator()): Likewise.
(__cust_access::_CData::operator()): Likewise.
(__cust_access::_CData::__as_const_pointer): Define for C++23
* include/bits/ranges_util.h (ranges::__detail::__different_from):
Make it an alias of std::__detail::__different_from.
(view_interface::cbegin): Define for C++23.
(view_interface::cend): Likewise.
* include/bits/stl_iterator.h (__detail::__different_from): Define.
(iter_const_reference_t): Define for C++23.
(__detail::__constant_iterator): Likewise.
(__detail::__is_const_iterator): Likewise.
(__detail::__not_a_const_iterator: Likewise.
(__detail::__iter_const_rvalue_reference_t): Likewise.
(__detail::__basic_const_iter_cat):: Likewise.
(const_iterator): Likewise.
(__detail::__const_sentinel): Likewise.
(const_sentinel): Likewise.
(basic_const_iterator): Likewise.
(common_type<basic_const_iterator<_Tp>, _Up>): Likewise.
(common_type<_Up, basic_const_iterator<_Tp>>): Likewise.
(common_type<basic_const_iterator<_Tp>, basic_const_iterator<Up>>):
Likewise.
(make_const_iterator): Define for C++23.
(make_const_sentinel): Likewise.
* include/std/ranges (__cpp_lib_ranges_as_const): Likewise.
(as_const_view): Likewise.
(enable_borrowed_range<as_const_view>): Likewise.
(views::__detail::__is_ref_view): Likewise.
(views::__detail::__can_is_const_view): Likewise.
(views::_AsConst, views::as_const): Likewise.
* include/std/span (span::const_iterator): Likewise.
(span::const_reverse_iterator): Likewise.
(span::cbegin): Likewise.
(span::cend): Likewise.
(span::crbegin): Likewise.
(span::crend): Likewise.
* include/std/version (__cpp_lib_ranges_as_const): Likewise.
* testsuite/std/ranges/adaptors/join.cc (test06): Adjust to
behave independently of C++20 vs C++23.
* testsuite/std/ranges/version_c++23.cc: Verify value of
__cpp_lib_ranges_as_const macro.
* testsuite/24_iterators/const_iterator/1.cc: New test.
* testsuite/std/ranges/adaptors/as_const/1.cc: New test.
---
libstdc++-v3/include/bits/ranges_base.h | 99 +++++
libstdc++-v3/include/bits/ranges_util.h | 22 +-
libstdc++-v3/include/bits/stl_iterator.h | 366 ++++++++++++++++++
libstdc++-v3/include/std/ranges | 106 +++++
libstdc++-v3/include/std/span | 22 ++
libstdc++-v3/include/std/version | 1 +
.../24_iterators/const_iterator/1.cc | 140 +++++++
.../std/ranges/adaptors/as_const/1.cc | 64 +++
.../testsuite/std/ranges/adaptors/join.cc | 5 +-
.../testsuite/std/ranges/version_c++23.cc | 4 +
10 files changed, 824 insertions(+), 5 deletions(-)
create mode 100644 libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index c89cb3e976a..b3144bbae4d 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -515,6 +515,17 @@ namespace ranges
template<range _Range>
using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
+#if __cplusplus > 202002L
+ template<range _Range>
+ using const_iterator_t = const_iterator<iterator_t<_Range>>;
+
+ template<range _Range>
+ using const_sentinel_t = const_sentinel<sentinel_t<_Range>>;
+
+ template<range _Range>
+ using range_const_reference_t = iter_const_reference_t<iterator_t<_Range>>;
+#endif
+
template<range _Range>
using range_difference_t = iter_difference_t<iterator_t<_Range>>;
@@ -607,8 +618,25 @@ namespace ranges
concept common_range
= range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
+#if __cplusplus > 202002L
+ template<typename _Tp>
+ concept constant_range
+ = input_range<_Tp> && std::__detail::__constant_iterator<iterator_t<_Tp>>;
+#endif
+
namespace __cust_access
{
+#if __cplusplus > 202020L
+ template<typename _Range>
+ constexpr auto&
+ __possibly_const_range(_Range& __r) noexcept
+ {
+ if constexpr (constant_range<const _Range> && !constant_range<_Range>)
+ return const_cast<const _Range&>(__r);
+ else
+ return __r;
+ }
+#else
// If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
template<typename _To, typename _Tp>
constexpr decltype(auto)
@@ -621,9 +649,23 @@ namespace ranges
else
return static_cast<const _Tp&&>(__t);
}
+#endif
struct _CBegin
{
+#if __cplusplus > 202002L
+ template<__maybe_borrowed_range _Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __t) const
+ noexcept(noexcept(std::make_const_iterator
+ (ranges::begin(__cust_access::__possibly_const_range(__t)))))
+ requires requires { std::make_const_iterator
+ (ranges::begin(__cust_access::__possibly_const_range(__t))); }
+ {
+ auto& __r = __cust_access::__possibly_const_range(__t);
+ return const_iterator_t<decltype(__r)>(ranges::begin(__r));
+ }
+#else
template<typename _Tp>
[[nodiscard]]
constexpr auto
@@ -633,10 +675,24 @@ namespace ranges
{
return _Begin{}(__cust_access::__as_const<_Tp>(__e));
}
+#endif
};
struct _CEnd final
{
+#if __cplusplus > 202002L
+ template<__maybe_borrowed_range _Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __t) const
+ noexcept(noexcept(std::make_const_sentinel
+ (ranges::end(__cust_access::__possibly_const_range(__t)))))
+ requires requires { std::make_const_sentinel
+ (ranges::end(__cust_access::__possibly_const_range(__t))); }
+ {
+ auto& __r = __cust_access::__possibly_const_range(__t);
+ return const_sentinel_t<decltype(__r)>(ranges::end(__r));
+ }
+#else
template<typename _Tp>
[[nodiscard]]
constexpr auto
@@ -646,10 +702,24 @@ namespace ranges
{
return _End{}(__cust_access::__as_const<_Tp>(__e));
}
+#endif
};
struct _CRBegin
{
+#if __cplusplus > 202002L
+ template<__maybe_borrowed_range _Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __t) const
+ noexcept(noexcept(std::make_const_iterator
+ (ranges::rbegin(__cust_access::__possibly_const_range(__t)))))
+ requires requires { std::make_const_iterator
+ (ranges::rbegin(__cust_access::__possibly_const_range(__t))); }
+ {
+ auto& __r = __cust_access::__possibly_const_range(__t);
+ return const_iterator<decltype(ranges::rbegin(__r))>(ranges::rbegin(__r));
+ }
+#else
template<typename _Tp>
[[nodiscard]]
constexpr auto
@@ -659,10 +729,24 @@ namespace ranges
{
return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
}
+#endif
};
struct _CREnd
{
+#if __cplusplus > 202002L
+ template<__maybe_borrowed_range _Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __t) const
+ noexcept(noexcept(std::make_const_sentinel
+ (ranges::rend(__cust_access::__possibly_const_range(__t)))))
+ requires requires { std::make_const_sentinel
+ (ranges::rend(__cust_access::__possibly_const_range(__t))); }
+ {
+ auto& __r = __cust_access::__possibly_const_range(__t);
+ return const_sentinel<decltype(ranges::rend(__r))>(ranges::rend(__r));
+ }
+#else
template<typename _Tp>
[[nodiscard]]
constexpr auto
@@ -672,10 +756,24 @@ namespace ranges
{
return _REnd{}(__cust_access::__as_const<_Tp>(__e));
}
+#endif
};
struct _CData
{
+#if __cplusplus > 202002L
+ template<typename _Tp>
+ static constexpr auto
+ __as_const_pointer(const _Tp* __p) noexcept
+ { return __p; }
+
+ template<__maybe_borrowed_range _Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __t) const
+ noexcept(noexcept(ranges::data(__cust_access::__possibly_const_range(__t))))
+ requires requires { ranges::data(__cust_access::__possibly_const_range(__t)); }
+ { return __as_const_pointer(ranges::data(__cust_access::__possibly_const_range(__t))); }
+#else
template<typename _Tp>
[[nodiscard]]
constexpr auto
@@ -685,6 +783,7 @@ namespace ranges
{
return _Data{}(__cust_access::__as_const<_Tp>(__e));
}
+#endif
};
} // namespace __cust_access
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index 880a0ce0143..f7e3538af97 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -53,9 +53,7 @@ namespace ranges
concept __has_arrow = input_iterator<_It>
&& (is_pointer_v<_It> || requires(_It __it) { __it.operator->(); });
- template<typename _Tp, typename _Up>
- concept __different_from
- = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
+ using std::__detail::__different_from;
} // namespace __detail
/// The ranges::view_interface class template
@@ -192,6 +190,24 @@ namespace ranges
constexpr decltype(auto)
operator[](range_difference_t<_Range> __n) const
{ return ranges::begin(_M_derived())[__n]; }
+
+#if __cplusplus > 202002L
+ constexpr auto
+ cbegin() requires input_range<_Derived>
+ { return ranges::cbegin(_M_derived()); }
+
+ constexpr auto
+ cbegin() const requires input_range<const _Derived>
+ { return ranges::cbegin(_M_derived()); }
+
+ constexpr auto
+ cend() requires input_range<_Derived>
+ { return ranges::cend(_M_derived()); }
+
+ constexpr auto
+ cend() const requires input_range<const _Derived>
+ { return ranges::cend(_M_derived()); }
+#endif
};
namespace __detail
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index a6a09dbac16..0974d735328 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -102,6 +102,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
using __clamp_iter_cat
= __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
+
+ template<typename _Tp, typename _Up>
+ concept __different_from
+ = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
}
#endif
@@ -2578,6 +2582,368 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
add_pointer_t<iter_reference_t<_It>>,
void>;
};
+
+#if __cplusplus > 202020L
+ template<indirectly_readable _It>
+ using iter_const_reference_t
+ = common_reference_t<const iter_value_t<_It>&&, iter_reference_t<_It>>;
+
+ template<input_iterator _It> class basic_const_iterator;
+
+ namespace __detail
+ {
+ template<typename _It>
+ concept __constant_iterator = input_iterator<_It>
+ && same_as<iter_const_reference_t<_It>, iter_reference_t<_It>>;
+
+ template<typename _Tp>
+ inline constexpr bool __is_const_iterator = false;
+
+ template<typename _It>
+ inline constexpr bool __is_const_iterator<basic_const_iterator<_It>> = true;
+
+ template<typename _Tp>
+ concept __not_a_const_iterator = !__is_const_iterator<_Tp>;
+
+ template<indirectly_readable _It>
+ using __iter_const_rvalue_reference_t
+ = common_reference_t<const iter_value_t<_It>&&, iter_rvalue_reference_t<_It>>;
+
+ template<typename _It>
+ struct __basic_const_iterator_iter_cat
+ { };
+
+ template<forward_iterator _It>
+ struct __basic_const_iterator_iter_cat<_It>
+ { using iterator_category = iterator_traits<_It>::iterator_category; };
+ } // namespace detail
+
+ template<input_iterator _It>
+ using const_iterator
+ = __conditional_t<__detail::__constant_iterator<_It>, _It, basic_const_iterator<_It>>;
+
+ namespace __detail
+ {
+ template<typename _Sent>
+ struct __const_sentinel
+ { using type = _Sent; };
+
+ template<input_iterator _Sent>
+ struct __const_sentinel<_Sent>
+ { using type = const_iterator<_Sent>; };
+ } // namespace __detail
+
+ template<semiregular _Sent>
+ using const_sentinel = typename __detail::__const_sentinel<_Sent>::type;
+
+ template<input_iterator _It>
+ class basic_const_iterator : public __detail::__basic_const_iterator_iter_cat<_It>
+ {
+ _It _M_current = _It();
+ using __reference = iter_const_reference_t<_It>;
+ using __rvalue_reference = __detail::__iter_const_rvalue_reference_t<_It>;
+
+ static auto
+ _S_iter_concept()
+ {
+ if constexpr (contiguous_iterator<_It>)
+ return contiguous_iterator_tag{};
+ else if constexpr (random_access_iterator<_It>)
+ return random_access_iterator_tag{};
+ else if constexpr (bidirectional_iterator<_It>)
+ return bidirectional_iterator_tag{};
+ else if constexpr (forward_iterator<_It>)
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }
+
+ template<input_iterator _It2> friend class basic_const_iterator;
+
+ public:
+ using iterator_concept = decltype(_S_iter_concept());
+ using value_type = iter_value_t<_It>;
+ using difference_type = iter_difference_t<_It>;
+
+ basic_const_iterator() requires default_initializable<_It> = default;
+
+ constexpr
+ basic_const_iterator(_It __current)
+ noexcept(is_nothrow_move_constructible_v<_It>)
+ : _M_current(std::move(__current))
+ { }
+
+ template<convertible_to<_It> _It2>
+ constexpr
+ basic_const_iterator(basic_const_iterator<_It2> __current)
+ noexcept(is_nothrow_constructible_v<_It, _It2>)
+ : _M_current(std::move(__current._M_current))
+ { }
+
+ template<__detail::__different_from<basic_const_iterator> _Tp>
+ requires convertible_to<_Tp, _It>
+ constexpr
+ basic_const_iterator(_Tp&& __current)
+ noexcept(is_nothrow_constructible_v<_It, _Tp>)
+ : _M_current(std::forward<_Tp>(__current))
+ { }
+
+ constexpr const _It&
+ base() const & noexcept
+ { return _M_current; }
+
+ constexpr _It
+ base() &&
+ noexcept(is_nothrow_move_constructible_v<_It>)
+ { return std::move(_M_current); }
+
+ constexpr __reference
+ operator*() const
+ noexcept(noexcept(static_cast<__reference>(*_M_current)))
+ { return static_cast<__reference>(*_M_current); }
+
+ constexpr const auto*
+ operator->() const
+ noexcept(contiguous_iterator<_It> || noexcept(*_M_current))
+ requires is_lvalue_reference_v<iter_reference_t<_It>>
+ && same_as<remove_cvref_t<iter_reference_t<_It>>, value_type>
+ {
+ if constexpr (contiguous_iterator<_It>)
+ return std::to_address(_M_current);
+ else
+ return std::__addressof(*_M_current);
+ }
+
+ constexpr basic_const_iterator&
+ operator++()
+ noexcept(noexcept(++_M_current))
+ {
+ ++_M_current;
+ return *this;
+ }
+
+ constexpr void
+ operator++(int)
+ noexcept(noexcept(++_M_current))
+ { ++_M_current; }
+
+ constexpr basic_const_iterator
+ operator++(int)
+ noexcept(noexcept(++*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
+ requires forward_iterator<_It>
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr basic_const_iterator&
+ operator--()
+ noexcept(noexcept(--_M_current))
+ requires bidirectional_iterator<_It>
+ {
+ --_M_current;
+ return *this;
+ }
+
+ constexpr basic_const_iterator
+ operator--(int)
+ noexcept(noexcept(--*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
+ requires bidirectional_iterator<_It>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ constexpr basic_const_iterator&
+ operator+=(difference_type __n)
+ noexcept(noexcept(_M_current += __n))
+ requires random_access_iterator<_It>
+ {
+ _M_current += __n;
+ return *this;
+ }
+
+ constexpr basic_const_iterator&
+ operator-=(difference_type __n)
+ noexcept(noexcept(_M_current -= __n))
+ requires random_access_iterator<_It>
+ {
+ _M_current -= __n;
+ return *this;
+ }
+
+ constexpr __reference
+ operator[](difference_type __n) const
+ noexcept(noexcept(static_cast<__reference>(_M_current[__n])))
+ requires random_access_iterator<_It>
+ { return static_cast<__reference>(_M_current[__n]); }
+
+ template<sentinel_for<_It> _Sent>
+ constexpr bool
+ operator==(const _Sent& __s) const
+ noexcept(noexcept(_M_current == __s))
+ { return _M_current == __s; }
+
+ constexpr bool
+ operator<(const basic_const_iterator& __y) const
+ noexcept(noexcept(_M_current < __y._M_current))
+ requires random_access_iterator<_It>
+ { return _M_current < __y._M_current; }
+
+ constexpr bool
+ operator>(const basic_const_iterator& __y) const
+ noexcept(noexcept(_M_current > __y._M_current))
+ requires random_access_iterator<_It>
+ { return _M_current > __y._M_current; }
+
+ constexpr bool
+ operator<=(const basic_const_iterator& __y) const
+ noexcept(noexcept(_M_current <= __y._M_current))
+ requires random_access_iterator<_It>
+ { return _M_current <= __y._M_current; }
+
+ constexpr bool
+ operator>=(const basic_const_iterator& __y) const
+ noexcept(noexcept(_M_current >= __y._M_current))
+ requires random_access_iterator<_It>
+ { return _M_current >= __y._M_current; }
+
+ constexpr auto
+ operator<=>(const basic_const_iterator& __y) const
+ noexcept(noexcept(_M_current <=> __y._M_current))
+ requires random_access_iterator<_It> && three_way_comparable<_It>
+ { return _M_current <=> __y._M_current; }
+
+ template<__detail::__different_from<basic_const_iterator> _It2>
+ constexpr bool
+ operator<(const _It2& __y) const
+ noexcept(noexcept(_M_current < __y))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return _M_current < __y; }
+
+ template<__detail::__different_from<basic_const_iterator> _It2>
+ constexpr bool
+ operator>(const _It2& __y) const
+ noexcept(noexcept(_M_current > __y))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return _M_current > __y; }
+
+ template<__detail::__different_from<basic_const_iterator> _It2>
+ constexpr bool
+ operator<=(const _It2& __y) const
+ noexcept(noexcept(_M_current <= __y))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return _M_current <= __y; }
+
+ template<__detail::__different_from<basic_const_iterator> _It2>
+ constexpr bool
+ operator>=(const _It2& __y) const
+ noexcept(noexcept(_M_current >= __y))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return _M_current >= __y; }
+
+ template<__detail::__different_from<basic_const_iterator> _It2>
+ constexpr auto
+ operator<=>(const _It2& __y) const
+ noexcept(noexcept(_M_current <=> __y))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ && three_way_comparable_with<_It, _It2>
+ { return _M_current <=> __y; }
+
+ template<__detail::__not_a_const_iterator _It2>
+ friend constexpr bool
+ operator<(const _It2& __x, const basic_const_iterator& __y)
+ noexcept(noexcept(__x < __y._M_current))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return __x < __y._M_current; }
+
+ template<__detail::__not_a_const_iterator _It2>
+ friend constexpr bool
+ operator>(const _It2& __x, const basic_const_iterator& __y)
+ noexcept(noexcept(__x > __y._M_current))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return __x > __y._M_current; }
+
+ template<__detail::__not_a_const_iterator _It2>
+ friend constexpr bool
+ operator<=(const _It2& __x, const basic_const_iterator& __y)
+ noexcept(noexcept(__x <= __y._M_current))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return __x <= __y._M_current; }
+
+ template<__detail::__not_a_const_iterator _It2>
+ friend constexpr bool
+ operator>=(const _It2& __x, const basic_const_iterator& __y)
+ noexcept(noexcept(__x >= __y._M_current))
+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
+ { return __x >= __y._M_current; }
+
+ friend constexpr basic_const_iterator
+ operator+(const basic_const_iterator& __i, difference_type __n)
+ noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
+ requires random_access_iterator<_It>
+ { return basic_const_iterator(__i._M_current + __n); }
+
+ friend constexpr basic_const_iterator
+ operator+(difference_type __n, const basic_const_iterator& __i)
+ noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
+ requires random_access_iterator<_It>
+ { return basic_const_iterator(__i._M_current + __n); }
+
+ friend constexpr basic_const_iterator
+ operator-(const basic_const_iterator& __i, difference_type __n)
+ noexcept(noexcept(basic_const_iterator(__i._M_current - __n)))
+ requires random_access_iterator<_It>
+ { return basic_const_iterator(__i._M_current - __n); }
+
+ template<sized_sentinel_for<_It> _Sent>
+ constexpr difference_type
+ operator-(const _Sent& __y) const
+ noexcept(noexcept(_M_current - __y))
+ { return _M_current - __y; }
+
+ template<__detail::__not_a_const_iterator _Sent>
+ requires sized_sentinel_for<_Sent, _It>
+ friend constexpr difference_type
+ operator-(const _Sent& __x, const basic_const_iterator& __y)
+ noexcept(noexcept(__x - __y._M_current))
+ { return __x - __y._M_current; }
+
+ friend constexpr __rvalue_reference
+ iter_move(const basic_const_iterator& __i)
+ noexcept(noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current))))
+ { return static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current)); }
+ };
+
+ template<typename _Tp, common_with<_Tp> _Up>
+ requires input_iterator<common_type_t<_Tp, _Up>>
+ struct common_type<basic_const_iterator<_Tp>, _Up>
+ { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
+
+ template<typename _Tp, common_with<_Tp> _Up>
+ requires input_iterator<common_type_t<_Tp, _Up>>
+ struct common_type<_Up, basic_const_iterator<_Tp>>
+ { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
+
+ template<typename _Tp, common_with<_Tp> _Up>
+ requires input_iterator<common_type_t<_Tp, _Up>>
+ struct common_type<basic_const_iterator<_Tp>, basic_const_iterator<_Up>>
+ { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
+
+ template<input_iterator _It>
+ constexpr const_iterator<_It>
+ make_const_iterator(_It __it)
+ noexcept(is_nothrow_convertible_v<_It, const_iterator<_It>>)
+ { return __it; }
+
+ template<semiregular _Sent>
+ constexpr const_sentinel<_Sent>
+ make_const_sentinel(_Sent __s)
+ noexcept(is_nothrow_convertible_v<_Sent, const_sentinel<_Sent>>)
+ { return __s; }
+#endif // C++23
#endif // C++20
/// @} group iterators
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 3f6ff505617..283d757faa4 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -8929,6 +8929,112 @@ namespace views::__adaptor
inline constexpr _Enumerate enumerate;
}
+
+#define __cpp_lib_ranges_as_const 202207L
+
+ template<view _Vp>
+ requires input_range<_Vp>
+ class as_const_view : public view_interface<as_const_view<_Vp>>
+ {
+ _Vp _M_base = _Vp();
+
+ public:
+ as_const_view() requires default_initializable<_Vp> = default;
+
+ constexpr explicit
+ as_const_view(_Vp __base)
+ noexcept(is_nothrow_move_constructible_v<_Vp>)
+ : _M_base(std::move(__base))
+ { }
+
+ constexpr _Vp
+ base() const &
+ noexcept(is_nothrow_copy_constructible_v<_Vp>)
+ requires copy_constructible<_Vp>
+ { return _M_base; }
+
+ constexpr _Vp
+ base() &&
+ noexcept(is_nothrow_move_constructible_v<_Vp>)
+ { return std::move(_M_base); }
+
+ constexpr auto
+ begin() requires (!__detail::__simple_view<_Vp>)
+ { return ranges::cbegin(_M_base); }
+
+ constexpr auto
+ begin() const requires range<const _Vp>
+ { return ranges::cbegin(_M_base); }
+
+ constexpr auto
+ end() requires (!__detail::__simple_view<_Vp>)
+ { return ranges::cend(_M_base); }
+
+ constexpr auto
+ end() const requires range<const _Vp>
+ { return ranges::cend(_M_base); }
+
+ constexpr auto
+ size() requires sized_range<_Vp>
+ { return ranges::size(_M_base); }
+
+ constexpr auto
+ size() const requires sized_range<const _Vp>
+ { return ranges::size(_M_base); }
+ };
+
+ template<typename _Range>
+ as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
+
+ template<typename _Tp>
+ inline constexpr bool enable_borrowed_range<as_const_view<_Tp>>
+ = enable_borrowed_range<_Tp>;
+
+ namespace views
+ {
+ namespace __detail
+ {
+ template<typename _Tp>
+ inline constexpr bool __is_ref_view = false;
+
+ template<typename _Range>
+ inline constexpr bool __is_ref_view<ref_view<_Range>> = true;
+
+ template<typename _Range>
+ concept __can_as_const_view = requires { as_const_view(std::declval<_Range>()); };
+ }
+
+ struct _AsConst : __adaptor::_RangeAdaptorClosure
+ {
+ template<viewable_range _Range>
+ constexpr auto
+ operator()(_Range&& __r) const
+ noexcept(noexcept(as_const_view(std::declval<_Range>())))
+ requires __detail::__can_as_const_view<_Range>
+ {
+ using _Tp = remove_cvref_t<_Range>;
+ using element_type = remove_reference_t<range_reference_t<_Range>>;
+ if constexpr (constant_range<views::all_t<_Range>>)
+ return views::all(std::forward<_Range>(__r));
+ else if constexpr (__detail::__is_empty_view<_Tp>)
+ return views::empty<const element_type>;
+ else if constexpr (std::__detail::__is_span<_Tp>)
+ return span<const element_type, _Tp::extent>(std::forward<_Range>(__r));
+ else if constexpr (__detail::__is_ref_view<_Tp>
+ && constant_range<const element_type>)
+ return ref_view(static_cast<const element_type&>
+ (std::forward<_Range>(__r).base()));
+ else if constexpr (is_lvalue_reference_v<_Range>
+ && constant_range<_Tp>
+ && !view<_Tp>)
+ return ref_view(static_cast<const _Tp&>(__r));
+ else
+ return as_const_view(std::forward<_Range>(__r));
+ }
+ };
+
+ inline constexpr _AsConst as_const;
+ }
#endif // C++23
} // namespace ranges
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 06d5c185880..67633899665 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -137,6 +137,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using const_reference = const element_type&;
using iterator = __gnu_cxx::__normal_iterator<pointer, span>;
using reverse_iterator = std::reverse_iterator<iterator>;
+#if __cplusplus > 202002L
+ using const_iterator = std::const_iterator<iterator>;
+ using const_reverse_iterator = std::const_iterator<reverse_iterator>;
+#endif
// member constants
static constexpr size_t extent = _Extent;
@@ -301,6 +305,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
rend() const noexcept
{ return reverse_iterator(this->begin()); }
+#if __cplusplus > 202002L
+ constexpr const_iterator
+ cbegin() const noexcept
+ { return begin(); }
+
+ constexpr const_iterator
+ cend() const noexcept
+ { return end(); }
+
+ constexpr const_reverse_iterator
+ crbegin() const noexcept
+ { return rbegin(); }
+
+ constexpr const_reverse_iterator
+ crend() const noexcept
+ { return rend(); }
+#endif
+
// subviews
template<size_t _Count>
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index d233b037d1a..9f31f25f1e9 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -339,6 +339,7 @@
#define __cpp_lib_ranges_stride 202207L
#define __cpp_lib_ranges_cartesian_product 202207L
#define __cpp_lib_ranges_as_rvalue 202207L
+#define __cpp_lib_ranges_as_const 202207L
#define __cpp_lib_ranges_enumerate 202302L
#define __cpp_lib_fold 202207L
#if __cpp_constexpr_dynamic_alloc
diff --git a/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
new file mode 100644
index 00000000000..51befd29541
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
@@ -0,0 +1,140 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <iterator>
+#include <array>
+#include <concepts>
+#include <string_view>
+#include <vector>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_input_range;
+using __gnu_test::test_forward_range;
+using __gnu_test::test_bidirectional_range;
+using __gnu_test::test_random_access_range;
+
+namespace ranges = std::ranges;
+
+template<class Iter, bool Const>
+void
+test01()
+{
+ if constexpr (Const)
+ {
+ static_assert( std::same_as<std::const_iterator<Iter>, Iter> );
+ static_assert( std::same_as<std::const_sentinel<Iter>, Iter> );
+ static_assert( std::same_as<std::iter_const_reference_t<Iter>,
+ std::iter_reference_t<Iter>> );
+ }
+ else
+ {
+ using Wrapped = std::basic_const_iterator<Iter>;
+
+ static_assert( std::same_as<std::const_iterator<Iter>, Wrapped> );
+ static_assert( std::same_as<std::const_sentinel<Iter>, Wrapped> );
+ static_assert( std::same_as<std::iter_const_reference_t<Iter>,
+ std::iter_reference_t<Wrapped>> );
+
+ static_assert( std::input_iterator<Iter> == std::input_iterator<Wrapped> );
+ static_assert( std::forward_iterator<Iter> == std::forward_iterator<Wrapped> );
+ static_assert( std::bidirectional_iterator<Iter> == std::bidirectional_iterator<Wrapped> );
+ static_assert( std::random_access_iterator<Iter> == std::random_access_iterator<Wrapped> );
+ }
+}
+
+template<class Range, bool Const>
+void
+test02()
+{
+ if constexpr (Const)
+ {
+ static_assert( ranges::constant_range<Range> );
+ static_assert( std::same_as<ranges::const_iterator_t<Range>, ranges::iterator_t<Range>> );
+ static_assert( std::same_as<ranges::const_sentinel_t<Range>, ranges::sentinel_t<Range>> );
+ static_assert( std::same_as<ranges::range_const_reference_t<Range>,
+ ranges::range_reference_t<Range>> );
+
+ static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
+ decltype(ranges::begin(std::declval<Range&>()))> );
+ static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
+ decltype(ranges::end(std::declval<Range&>()))> );
+ }
+ else
+ {
+ static_assert( !ranges::constant_range<Range> );
+ using Wrapped = std::basic_const_iterator<ranges::iterator_t<Range>>;
+
+ static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped> );
+ if constexpr (ranges::common_range<Range>)
+ static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped> );
+ static_assert( std::same_as<ranges::range_const_reference_t<Range>,
+ std::iter_reference_t<Wrapped>> );
+
+ static_assert( ranges::input_range<Range> == std::input_iterator<Wrapped> );
+ static_assert( ranges::forward_range<Range> == std::forward_iterator<Wrapped> );
+ static_assert( ranges::bidirectional_range<Range> == std::bidirectional_iterator<Wrapped> );
+ static_assert( ranges::random_access_range<Range> == std::random_access_iterator<Wrapped> );
+
+ if constexpr (ranges::constant_range<const Range&>)
+ {
+ static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
+ decltype(ranges::begin(std::declval<const Range&>()))> );
+ static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
+ decltype(ranges::end(std::declval<const Range&>()))> );
+ }
+ else
+ {
+ static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())), Wrapped> );
+ if constexpr (ranges::common_range<Range>)
+ static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())), Wrapped> );
+ }
+ }
+}
+
+void
+test03()
+{
+ static_assert( std::same_as<std::const_sentinel<std::unreachable_sentinel_t>,
+ std::unreachable_sentinel_t> );
+}
+
+int
+main()
+{
+ test01<int*, false>();
+ test01<ranges::iterator_t<test_input_range<int>>, false>();
+ test01<ranges::iterator_t<test_forward_range<int>>, false>();
+ test01<ranges::iterator_t<test_bidirectional_range<int>>, false>();
+ test01<ranges::iterator_t<test_random_access_range<int>>, false>();
+ test01<std::array<int, 3>::iterator, false>();
+ test01<std::vector<bool>::iterator, false>();
+
+ test01<const int*, true>();
+ test01<ranges::iterator_t<test_input_range<const int>>, true>();
+ test01<ranges::iterator_t<test_forward_range<const int>>, true>();
+ test01<ranges::iterator_t<test_bidirectional_range<const int>>, true>();
+ test01<ranges::iterator_t<test_random_access_range<const int>>, true>();
+ test01<std::array<const int, 3>::iterator, true>();
+ test01<std::string_view::iterator, true>();
+ test01<std::vector<bool>::const_iterator, true>();
+
+ test02<int[42], false>();
+ test02<test_input_range<int>, false>();
+ test02<test_forward_range<int>, false>();
+ test02<test_bidirectional_range<int>, false>();
+ test02<test_random_access_range<int>, false>();
+ test02<std::array<int, 3>, false>();
+ test02<std::vector<bool>, false>();
+
+ test02<const int[42], true>();
+ test02<test_input_range<const int>, true>();
+ test02<test_forward_range<const int>, true>();
+ test02<test_bidirectional_range<const int>, true>();
+ test02<test_random_access_range<const int>, true>();
+ test02<std::array<const int, 3>, true>();
+ test02<const std::array<int, 3>, true>();
+ test02<std::string_view, true>();
+ test02<const std::vector<bool>, true>();
+
+ test03();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
new file mode 100644
index 00000000000..d04645f047e
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
@@ -0,0 +1,64 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <ranges>
+#include <algorithm>
+#include <span>
+#include <utility>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+#if __cpp_lib_ranges_as_const != 202207L
+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <ranges>"
+#endif
+
+namespace ranges = std::ranges;
+namespace views = std::views;
+
+constexpr bool
+test01()
+{
+ int x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ auto v = x | views::filter([](int x) { return (x % 2) == 0; }) | views::as_const;
+
+ using ty = decltype(v);
+ static_assert(ranges::constant_range<ty>);
+ static_assert(!ranges::constant_range<decltype(v.base())>);
+ static_assert(std::same_as<ranges::range_reference_t<ty>, const int&>);
+ static_assert(std::same_as<ranges::range_reference_t<decltype(v.base())>, int&>);
+
+ VERIFY( ranges::equal(v, (int[]){2, 4, 6, 8, 10}) );
+ VERIFY( ranges::equal(v | views::reverse, (int[]){10, 8, 6, 4, 2}) );
+
+ return true;
+}
+
+constexpr bool
+test02()
+{
+ std::same_as<ranges::empty_view<const int>> auto v1
+ = views::empty<int> | views::as_const;
+
+ int x[] = {1, 2, 3};
+ std::same_as<ranges::as_const_view<ranges::ref_view<int[3]>>> auto v2
+ = x | views::as_const;
+ std::same_as<ranges::ref_view<const int[3]>> auto v3
+ = std::as_const(x) | views::as_const;
+ std::same_as<ranges::ref_view<const int[3]>> auto v4
+ = std::as_const(x) | views::all | views::as_const;
+ std::same_as<std::span<const int>> auto v5
+ = std::span{x, x+3} | views::as_const;
+
+ std::same_as<ranges::as_const_view<ranges::chunk_view<ranges::ref_view<int[3]>>>> auto v6
+ = x | views::chunk(2) | views::as_const;
+ VERIFY( v6.size() == 2 );
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index e5ae9b7be20..cda2cbfc577 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -24,6 +24,7 @@
#include <sstream>
#include <string>
#include <string_view>
+#include <utility>
#include <vector>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
@@ -120,8 +121,8 @@ test06()
// Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
static_assert(!ranges::common_range<decltype(v)>);
static_assert(!std::same_as<decltype(ranges::end(v)),
- decltype(ranges::cend(v))>);
- auto b = ranges::cend(v);
+ decltype(std::as_const(v).end())>);
+ auto b = std::as_const(v).end();
b = ranges::end(v);
}
diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
index fa010bf166b..e2c14edc8ef 100644
--- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
+++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
@@ -45,6 +45,10 @@
# error "Feature-test macro __cpp_lib_ranges_as_rvalue has wrong value in <version>"
#endif
+#if __cpp_lib_ranges_as_const != 202207L
+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <version>"
+#endif
+
#if __cpp_lib_ranges_enumerate != 202302L
# error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value in <version>"
#endif
--
2.40.0.335.g9857273be0
next prev parent reply other threads:[~2023-04-14 4:00 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-14 4:00 [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Patrick Palka
2023-04-14 4:00 ` Patrick Palka [this message]
2023-04-14 9:52 ` [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator" Ville Voutilainen
2023-04-14 10:14 ` Jonathan Wakely
2023-04-14 14:14 ` Patrick Palka
2023-04-14 9:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230414040042.1498825-2-ppalka@redhat.com \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).